Browse Source

[frontend]Implement pathbrowser in storage browser with improvements (#3445)

* [frontend]Implement pathbrowser in storage browser with improvements
Nidhi Bhat G 2 years ago
parent
commit
a903e183e1

+ 14 - 2
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTabContents/StorageBrowserTabContent.scss

@@ -17,7 +17,7 @@
 
 .hue-storage-browser-tabContent {
   background-color: vars.$fluidx-white;
-  margin: 16px 0;
+  margin: vars.$fluidx-spacing-s 0;
   padding: 16px;
   //TODO: Set height to content
 
@@ -26,7 +26,7 @@
     align-items: center;
 
     .hue-storage-browser__icon {
-      margin-right: 10px;
+      margin-right: vars.$fluidx-spacing-xs;
       flex: 0 0 auto;
       height: 24px;
       width: 24px;
@@ -39,4 +39,16 @@
       font-weight: vars.$fluidx-heading-h3-weight;
     }
   }
+
+  .hue-storage-browser__path-browser-panel {
+    display: flex;
+    margin-top: vars.$fluidx-spacing-xxs;
+    //TODO: Remove nesting of classes.
+    .hue-storage-browser__filePath {
+      flex: 0 0 auto;
+      color: vars.$text-color;
+      font-weight: 600;
+      margin-right: vars.$fluidx-spacing-xs;
+    }
+  }
 }

+ 17 - 1
desktop/core/src/desktop/js/apps/storageBrowser/StorageBrowserPage/StorageBrowserTabContents/StorageBrowserTabContent.tsx

@@ -17,7 +17,9 @@
 import React, { useState, useEffect } from 'react';
 import { Spin } from 'antd';
 
+import { i18nReact } from '../../../../utils/i18nReact';
 import BucketIcon from '@cloudera/cuix-core/icons/react/BucketIcon';
+import PathBrowser from '../../../../reactComponents/FileChooser/PathBrowser/PathBrowser';
 import { fetchFiles } from '../../../../reactComponents/FileChooser/api';
 import { PathAndFileData } from '../../../../reactComponents/FileChooser/types';
 
@@ -40,6 +42,8 @@ const StorageBrowserTabContent: React.FC<StorageBrowserTabContentProps> = ({
   const [filesData, setFilesData] = useState<PathAndFileData | undefined>();
   const [loadingFiles, setloadingFiles] = useState(true);
 
+  const { t } = i18nReact.useTranslation();
+
   useEffect(() => {
     setloadingFiles(true);
     fetchFiles(filePath)
@@ -60,9 +64,21 @@ const StorageBrowserTabContent: React.FC<StorageBrowserTabContentProps> = ({
         <div className="hue-storage-browser__title-bar" data-testid={`${testId}-title-bar`}>
           <BucketIcon className="hue-storage-browser__icon" data-testid={`${testId}-icon`} />
           <div className="hue-storage-browser__folder-name" data-testid={`${testId}-folder-namer`}>
-            {filesData?.breadcrumbs[filesData?.breadcrumbs.length - 1].label}
+            {filesData?.breadcrumbs[filesData?.breadcrumbs?.length - 1].label}
           </div>
         </div>
+        <div
+          className="hue-storage-browser__path-browser-panel"
+          data-testid={`${testId}-path-browser-panel`}
+        >
+          <div className="hue-storage-browser__filePath">{t('File Path:')}</div>
+          <PathBrowser
+            breadcrumbs={filesData?.breadcrumbs}
+            onFilepathChange={setFilePath}
+            seperator={'/'}
+            showIcon={false}
+          />
+        </div>
       </div>
     </Spin>
   );

+ 3 - 1
desktop/core/src/desktop/js/reactComponents/FileChooser/FileChooserModal/FileChooserModal.tsx

@@ -127,8 +127,10 @@ const FileChooserModal: React.FC<FileProps> = ({ show, onCancel, title, okText }
               <Row className="hue-path-browser-panel" onClick={e => e.stopPropagation()}>
                 <Col span={18}>
                   <PathBrowser
-                    handleFilePathChange={setFilePath}
                     breadcrumbs={filesData?.breadcrumbs}
+                    onFilepathChange={setFilePath}
+                    seperator={'>'}
+                    showIcon={true}
                   ></PathBrowser>
                 </Col>
                 <Col span={3}>

+ 24 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/Breadcrumb/Breadcrumb.scss

@@ -0,0 +1,24 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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.
+
+.hue-path-browser__breadcrumb {
+  display: contents;
+
+  .hue-path-browser__overflowing-label {
+    min-width: var(--minWidth);
+  }
+}
+

+ 47 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/Breadcrumb/Breadcrumb.tsx

@@ -0,0 +1,47 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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 { Property } from '@typescript-eslint/types/dist/ast-spec';
+import { min } from 'lodash';
+import React from 'react';
+
+import OverflowingItem from '../OverflowingItem';
+import './Breadcrumb.scss';
+
+interface BreadcrumbProps {
+  label: string;
+  url: string;
+  onFilepathChange: (path: string) => void;
+}
+
+const Breadcrumb: React.FC<BreadcrumbProps> = ({ label, url, onFilepathChange }) => {
+  const handleFilepathChange = () => {
+    onFilepathChange(url);
+  };
+
+  const minWidth = '' + (label.length < 10 ? label.length : 10) + 'ch';
+
+  return (
+    <div
+      className="hue-path-browser__breadcrumb"
+      style={{ '--minWidth': `${minWidth}` } as React.CSSProperties}
+    >
+      <OverflowingItem onClick={handleFilepathChange} label={label} />
+    </div>
+  );
+};
+
+export default Breadcrumb;

+ 22 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/DropdownMenuItem/DropdownMenuItem.scss

@@ -0,0 +1,22 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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.
+
+.hue-path-browser__dropdown-item {
+  .hue-path-browser__overflowing-label {
+    width: 100%;
+    text-align: left;
+  }
+}

+ 40 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/DropdownMenuItem/DropdownMenuItem.tsx

@@ -0,0 +1,40 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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 React from 'react';
+
+import OverflowingItem from '../OverflowingItem';
+import './DropdownMenuItem.scss';
+
+interface DropDownMenuItemProps {
+  label: string;
+  url: string;
+  onFilepathChange: (path: string) => void;
+}
+
+const DropDownMenuItem: React.FC<DropDownMenuItemProps> = ({ label, url, onFilepathChange }) => {
+  const handleFilepathChange = () => {
+    onFilepathChange(url);
+  };
+
+  return (
+    <div className="hue-path-browser__dropdown-item">
+      <OverflowingItem label={label} onClick={handleFilepathChange}></OverflowingItem>
+    </div>
+  );
+};
+
+export default DropDownMenuItem;

+ 9 - 2
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowTooltip.tsx

@@ -22,16 +22,22 @@ interface OverflowTooltipProps {
   isOverflowing: boolean;
   toolTipTriggers: string | string[];
   children: ReactNode | ReactElement;
+  testId?: string;
 }
 
+const defaultProps = {
+  testId: 'hue-overflowing'
+};
+
 const OverflowTooltip: React.FC<OverflowTooltipProps> = ({
   title,
   isOverflowing,
   toolTipTriggers,
-  children
+  children,
+  testId
 }) => {
   return isOverflowing ? (
-    <Tooltip title={title} trigger={toolTipTriggers}>
+    <Tooltip title={title} trigger={toolTipTriggers} data-testid={`${testId}-tooltip`}>
       {children}
     </Tooltip>
   ) : (
@@ -39,4 +45,5 @@ const OverflowTooltip: React.FC<OverflowTooltipProps> = ({
   );
 };
 
+OverflowTooltip.defaultProps = defaultProps;
 export default OverflowTooltip;

+ 2 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowingItem.scss

@@ -13,9 +13,11 @@
 // 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.
+@use 'variables' as vars;
 
 .hue-path-browser__overflowing-label {
   background-color: transparent;
+  color: vars.$fluidx-blue-400;
   white-space: nowrap;
   padding: 0;
   overflow: hidden;

+ 8 - 26
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/OverflowingItem.tsx

@@ -20,36 +20,19 @@ import { Button } from 'antd';
 import OverflowTooltip from './OverflowTooltip';
 import './OverflowingItem.scss';
 
-type OverflowingComponentType = 'breadcrumb' | 'menu';
 interface OverflowingItemProps {
   label: string;
-  url: string;
-  handleFilePathChange: (path: string) => void;
-  componentType: OverflowingComponentType;
+  onClick: () => void;
+  testId?: string;
 }
 
-//TODO: change to add classnames to components instead of inline styles
-const customStyles: {
-  [key in OverflowingComponentType]: React.CSSProperties;
-} = {
-  breadcrumb: {
-    minWidth: '5ch'
-  },
-  menu: {
-    width: '100%',
-    textAlign: 'left'
-  }
+const defaultProps = {
+  testId: 'hue-path-browser__overflowing'
 };
 
-const OverflowingItem: React.FC<OverflowingItemProps> = ({
-  label,
-  url,
-  handleFilePathChange,
-  componentType
-}) => {
+const OverflowingItem: React.FC<OverflowingItemProps> = ({ label, onClick, testId }) => {
   const textElementRef = useRef<HTMLDivElement>(null);
   const [isOverflown, setIsOverflown] = useState(false);
-
   const compareSize = () => {
     const element = textElementRef.current;
 
@@ -72,10 +55,8 @@ const OverflowingItem: React.FC<OverflowingItemProps> = ({
       <Button
         ref={textElementRef}
         className="hue-path-browser__overflowing-label"
-        onClick={() => {
-          handleFilePathChange(url);
-        }}
-        style={customStyles[componentType]}
+        onClick={onClick}
+        data-testid={`${testId}-label`}
       >
         {label}
       </Button>
@@ -83,4 +64,5 @@ const OverflowingItem: React.FC<OverflowingItemProps> = ({
   );
 };
 
+OverflowingItem.defaultProps = defaultProps;
 export default OverflowingItem;

+ 27 - 15
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.scss

@@ -16,24 +16,28 @@
 
 @use 'variables' as vars;
 
+$icon-width: 24px;
+$icon-height: 24px;
+
 .hue-path-browser {
   display: flex;
+  width: 100%;
 
-  .hue-filesystem__icon {
+  .hue-path-browser__file-system-icon {
     object-fit: cover;
-    width: 24px;
-    height: 24px;
-    margin-left: 10px;
-    margin-right: 10px;
+    width: $icon-width;
+    height: $icon-height;
+    margin: 0 vars.$fluidx-spacing-xs;
   }
 
-  .hue-toggle-breadcrumb-input:empty {
+  .hue-path-browser__toggle-breadcrumb-input-btn:empty {
     pointer-events: auto;
     width: 60px;
     visibility: visible;
     border: none;
     box-shadow: none;
     padding: 0;
+    background-color: vars.$fluidx-white;
 
     &:hover {
       cursor: text;
@@ -46,12 +50,12 @@
   }
 }
 
-.hue-path-browser__breadcrumb {
+.hue-path-browser__breadcrumbs {
   /* stylelint-disable no-invalid-position-at-import-rule */
   @import './OverflowingItem.scss';
 
   display: flex;
-  gap: 10px;
+  gap: vars.$fluidx-spacing-xs;
   max-width: 60%;
 
   &:hover {
@@ -61,6 +65,7 @@
   .hue-path-browser__dropdown-button {
     border: none;
     box-shadow: none;
+    background-color: transparent;
 
     &:hover {
       background-color: vars.$fluidx-gray-100;
@@ -70,21 +75,26 @@
       background-color: vars.$fluidx-gray-100;
     }
   }
+
+  .hue-path-browser__breadcrumb:last-of-type button:last-of-type {
+    color: vars.$fluidx-gray-700;
+  }
 }
 
 .hue-path-browser__breadcrumb-seperator {
-  width: 14px;
-  height: 28px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
   flex: 0 0 auto;
 }
 
 .hue-path-browser__dropdown {
   width: 200px;
+  background-color: vars.$fluidx-gray-200;
 
-  .hue-path-browser__dropdown {
-    background-color: vars.$fluidx-gray-200;
-    overflow-y: scroll;
+  .ant-dropdown-menu {
     max-height: 200px;
+    overflow-y: scroll;
   }
 
   .ant-dropdown-menu-title-content {
@@ -98,10 +108,12 @@
 .hue-path-browser-panel {
   .hue-path-browser__input {
     width: 300px;
+    flex: 0 0 auto;
 
     .ant-input-prefix {
-      width: 24px;
-      height: 24px;
+      width: $icon-width;
+      height: $icon-height;
+      object-fit: cover;
     }
   }
 

+ 201 - 0
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.test.tsx

@@ -0,0 +1,201 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. 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 React from 'react';
+import { render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import '@testing-library/jest-dom';
+
+import PathBrowser from './PathBrowser';
+
+const onFilepathChangeMock = jest.fn();
+
+const breadcrumbsTestConfig1 = [
+  {
+    url: 'abfs://',
+    label: 'abfs://'
+  },
+  {
+    url: 'abfs://test',
+    label: 'test'
+  },
+  {
+    url: 'abfs://test/test1',
+    label: 'test1'
+  }
+];
+
+const breadcrumbsTestConfig2 = [
+  {
+    url: 'abfs://',
+    label: 'abfs://'
+  },
+  {
+    url: 'abfs://test',
+    label: 'test'
+  },
+  {
+    url: 'abfs://test/test1',
+    label: 'test1'
+  },
+  {
+    url: 'abfs://test/test1/test2',
+    label: 'test2'
+  },
+  {
+    url: 'abfs://test/test1/test2/a very very very long test label',
+    label: 'a very very very long test label'
+  },
+  {
+    url: 'abfs://test/test1/test2/a very very very long test label/a very very very long test label 1',
+    label: 'a very very very long test label 1'
+  },
+  {
+    url: 'abfs://test/test1/test2/a very very very long test label/a very very very long test label 1/a very very very long test label 2',
+    label: 'a very very very long test label 2'
+  }
+];
+
+afterEach(() => {
+  jest.clearAllMocks();
+});
+
+describe('Pathbrowser', () => {
+  describe('Pathbrowser breadcrumbs', () => {
+    test('renders the specified seperator to seperate the breadcrumbs', () => {
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'%'}
+          showIcon
+        />
+      );
+      const seperator = screen.getAllByText('%');
+      expect(seperator).not.toBeNull();
+    });
+
+    test('does not render a different seperator than specified to seperate the breadcrumbs', () => {
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'%'}
+          showIcon
+        />
+      );
+      expect(screen.queryByText('/')).toBeNull();
+    });
+
+    test('renders breadcrumbs without dropdown button if there are less than or equal to 3 breadcrumbs', () => {
+      const rendered = render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'/'}
+          showIcon
+        />
+      );
+      expect(rendered.queryByRole('button', { name: '..' })).toBeNull();
+    });
+
+    test('renders breadcrumbs with dropdown button if there are more than 3 breadcrumbs', () => {
+      const rendered = render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig2}
+          seperator={'/'}
+          showIcon
+        />
+      );
+      expect(rendered.getByRole('button', { name: '..' })).toBeVisible();
+    });
+
+    test('renders dropdown on click of dropdown button', async () => {
+      const user = userEvent.setup();
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig2}
+          seperator={'/'}
+          showIcon
+        />
+      );
+      //From the given testconfig: The dropdown menu would consist of menu button with label test2. 'test2' should not be visible until the dropdown button is clicked.
+      expect(screen.queryByRole('button', { name: 'test2' })).not.toBeInTheDocument();
+      const dropdownButton = await screen.getByRole('button', { name: '..' });
+      await user.click(dropdownButton);
+      expect(screen.getByRole('button', { name: 'test2' })).toBeInTheDocument();
+    });
+
+    test('calls onFilepathChange on click of breadcrumb', async () => {
+      const user = userEvent.setup();
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'/'}
+          showIcon={false}
+        />
+      );
+      expect(onFilepathChangeMock).not.toHaveBeenCalled();
+      const breadcrumb = screen.getByRole('button', { name: 'test' });
+      await user.click(breadcrumb);
+      expect(onFilepathChangeMock).toHaveBeenCalled();
+    });
+
+    test('renders icon in breadcrumbs only if specified', () => {
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'/'}
+          showIcon
+        />
+      );
+      const icon = screen.getByTestId('hue-path-browser__file-system-icon');
+      expect(icon).toBeVisible();
+    });
+
+    test('does not render icon in breadcrumbs if showIcon is false', () => {
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'/'}
+          showIcon={false}
+        />
+      );
+      const icon = screen.queryByTestId('hue-path-browser__file-system-icon');
+      expect(icon).toBeNull();
+    });
+  });
+
+  describe('Pathbrowser Input', () => {
+    test('input is hidden before toggle button is clicked', () => {
+      render(
+        <PathBrowser
+          onFilepathChange={onFilepathChangeMock}
+          breadcrumbs={breadcrumbsTestConfig1}
+          seperator={'/'}
+          showIcon
+        />
+      );
+      const input = screen.queryByDisplayValue('abfs://test/test1');
+      expect(input).toBeNull();
+    });
+  });
+});

+ 80 - 38
desktop/core/src/desktop/js/reactComponents/FileChooser/PathBrowser/PathBrowser.tsx

@@ -15,24 +15,37 @@
 // limitations under the License.
 
 import React, { useRef, useEffect, useState, RefObject } from 'react';
-import { Input, Button, Dropdown, Row, Col } from 'antd';
+import { Input, Button, Dropdown } from 'antd';
 import type { MenuProps } from 'antd';
-import { RightOutlined } from '@ant-design/icons';
 
 import HdfsIcon from '../../../components/icons/HdfsIcon';
 import S3Icon from '../../../components/icons/S3Icon';
 import AdlsIcon from '../../../components/icons/AdlsIcon';
 
 import { BreadcrumbData } from '../types';
-import OverflowingItem from './OverflowingItem';
+import Breadcrumb from './Breadcrumb/Breadcrumb';
+import DropDownMenuItem from './DropdownMenuItem/DropdownMenuItem';
 import './PathBrowser.scss';
 
 interface PathBrowserProps {
   breadcrumbs?: BreadcrumbData[];
-  handleFilePathChange: (path: string) => void;
+  onFilepathChange: (path: string) => void;
+  seperator: string;
+  showIcon: boolean;
+  testId?: string;
 }
 
-const PathBrowser: React.FC<PathBrowserProps> = ({ breadcrumbs, handleFilePathChange }) => {
+const defaultProps = {
+  testId: 'hue-path-browser'
+};
+
+const PathBrowser: React.FC<PathBrowserProps> = ({
+  breadcrumbs,
+  onFilepathChange,
+  seperator,
+  showIcon,
+  testId
+}) => {
   const [isEditMode, setIsEditMode] = useState(false);
 
   const icons = {
@@ -78,11 +91,11 @@ const PathBrowser: React.FC<PathBrowserProps> = ({ breadcrumbs, handleFilePathCh
       return {
         key: breadcrumb.url,
         label: (
-          <OverflowingItem
+          <DropDownMenuItem
+            key={breadcrumb.url}
             label={breadcrumb.label}
             url={breadcrumb.url}
-            handleFilePathChange={handleFilePathChange}
-            componentType="menu"
+            onFilepathChange={onFilepathChange}
           />
         )
       };
@@ -94,40 +107,51 @@ const PathBrowser: React.FC<PathBrowserProps> = ({ breadcrumbs, handleFilePathCh
     return (
       <>
         {!isEditMode ? (
-          <div className="hue-path-browser">
-            <div className="hue-filesystem__icon">
-              {icons[extractFileSystem(breadcrumbs[0].label)]}
-            </div>
-            <div className="hue-path-browser__breadcrumb">
+          <div className="hue-path-browser" data-testid={`${testId}`}>
+            {showIcon && (
+              <div
+                className="hue-path-browser__file-system-icon"
+                data-testid={`${testId}__file-system-icon`}
+              >
+                {icons[extractFileSystem(breadcrumbs[0].label)]}
+              </div>
+            )}
+            <div className="hue-path-browser__breadcrumbs" data-testid={`${testId}-breadcrumb`}>
               {breadcrumbs.length <= 3 ? (
                 breadcrumbs.map((item: BreadcrumbData, index: number) => {
                   return (
-                    <>
-                      <OverflowingItem
+                    <React.Fragment key={item.url + index}>
+                      <Breadcrumb
                         key={item.url}
                         label={index === 0 ? extractFileSystem(item.label) : item.label}
                         url={item.url}
-                        handleFilePathChange={handleFilePathChange}
-                        componentType="breadcrumb"
+                        onFilepathChange={onFilepathChange}
                       />
-                      {index != breadcrumbs.length - 1 ? (
-                        <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
-                      ) : (
-                        <></>
+                      {index != breadcrumbs.length - 1 && (
+                        <div
+                          className="hue-path-browser__breadcrumb-seperator"
+                          data-testid={`${testId}-breadcrumb-seperator`}
+                        >
+                          {seperator}
+                        </div>
                       )}
-                    </>
+                    </React.Fragment>
                   );
                 })
               ) : (
                 <>
-                  <OverflowingItem
+                  <Breadcrumb
                     label={extractFileSystem(breadcrumbs[0].label)}
                     url={breadcrumbs[0].url}
-                    handleFilePathChange={handleFilePathChange}
-                    componentType="breadcrumb"
+                    onFilepathChange={onFilepathChange}
                     key={breadcrumbs[0].url}
                   />
-                  <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
+                  <div
+                    className="hue-path-browser__breadcrumb-seperator"
+                    data-testid={`${testId}-breadcrumb-seperator`}
+                  >
+                    {seperator}
+                  </div>
                   <Dropdown
                     overlayClassName="hue-path-browser__dropdown"
                     menu={{
@@ -136,46 +160,63 @@ const PathBrowser: React.FC<PathBrowserProps> = ({ breadcrumbs, handleFilePathCh
                     }}
                     trigger={['hover', 'click']}
                     autoFocus
+                    data-testid={`${testId}-dropdown`}
                   >
-                    <Button className="hue-path-browser__dropdown-button">..</Button>
+                    <Button
+                      className="hue-path-browser__dropdown-button"
+                      data-testid={`${testId}-dropdown-btn`}
+                    >
+                      ..
+                    </Button>
                   </Dropdown>
-                  <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
-                  <OverflowingItem
+                  <div
+                    className="hue-path-browser__breadcrumb-seperator"
+                    data-testid={`${testId}-breadcrumb-seperator`}
+                  >
+                    {seperator}
+                  </div>
+                  <Breadcrumb
                     key={breadcrumbs[breadcrumbs.length - 2].url}
                     label={breadcrumbs[breadcrumbs.length - 2].label}
                     url={breadcrumbs[breadcrumbs.length - 2].url}
-                    handleFilePathChange={handleFilePathChange}
-                    componentType="breadcrumb"
+                    onFilepathChange={onFilepathChange}
                   />
-                  <RightOutlined className="hue-path-browser__breadcrumb-seperator" />
-                  <OverflowingItem
+                  <div
+                    className="hue-path-browser__breadcrumb-seperator"
+                    data-testid={`${testId}-breadcrumb-seperator`}
+                  >
+                    {seperator}
+                  </div>
+                  <Breadcrumb
                     key={breadcrumbs[breadcrumbs.length - 1].url}
                     label={breadcrumbs[breadcrumbs.length - 1].label}
                     url={breadcrumbs[breadcrumbs.length - 1].url}
-                    handleFilePathChange={handleFilePathChange}
-                    componentType="breadcrumb"
+                    onFilepathChange={onFilepathChange}
                   />
                 </>
               )}
             </div>
             <Button
-              className="hue-toggle-breadcrumb-input"
+              className="hue-path-browser__toggle-breadcrumb-input-btn"
+              aria-label="hue-path-browser__toggle-breadcrumb-input-btn"
               title="Edit path"
               onClick={() => {
                 setIsEditMode(true);
               }}
+              data-testid={`${testId}-toggle-input-btn`}
             ></Button>
           </div>
         ) : (
           <div ref={wrapperRef}>
             <Input
-              prefix={icons[extractFileSystem(breadcrumbs[0].label)]}
+              prefix={showIcon ? icons[extractFileSystem(breadcrumbs[0].label)] : <span />}
               defaultValue={decodeURIComponent(breadcrumbs[breadcrumbs.length - 1].url)}
               onPressEnter={customPath => {
-                handleFilePathChange((customPath.target as HTMLInputElement).value);
+                onFilepathChange((customPath.target as HTMLInputElement).value);
               }}
               className="hue-path-browser__input"
               autoFocus
+              data-testid={`${testId}-input`}
             ></Input>
           </div>
         )}
@@ -184,4 +225,5 @@ const PathBrowser: React.FC<PathBrowserProps> = ({ breadcrumbs, handleFilePathCh
   }
 };
 
+PathBrowser.defaultProps = defaultProps;
 export default PathBrowser;