Brijesh619 commented on code in PR #688:
URL: https://github.com/apache/atlas/pull/688#discussion_r3720030932
##########
dashboard/src/views/SideBar/__tests__/SideBarBody.test.tsx:
##########
@@ -636,4 +641,85 @@ describe('SideBarBody', () => {
expect(screen.getByTestId('entities-tree')).toBeInTheDocument();
});
});
+
+ describe('Collapsed Sidebar Popovers', () => {
+ beforeEach(() => {
+ // Start with closed drawer to see popover icons
+ renderWithProviders();
+ const toggleButton =
screen.getByTestId('KeyboardDoubleArrowLeftIcon').closest('button');
+ fireEvent.click(toggleButton!);
+ });
+
+ it('should open correct popover when module icon is clicked', async () => {
+ // Find the glossary icon and click it
+ const glossaryIcon = screen.getByAltText('glossary');
+ fireEvent.click(glossaryIcon.closest('button')!);
+
+ await waitFor(() => {
+ // Popover should render the glossary tree
+ const glossaryTrees = screen.getAllByTestId('glossary-tree');
+ expect(glossaryTrees.length).toBeGreaterThan(0);
+ });
+ });
+
+ it('should share search term between sidebar and popover', async () => {
+ // Re-open sidebar to access main search input
+ const toggleOpenButton =
screen.getByTestId('KeyboardDoubleArrowRightIcon').closest('button');
+ fireEvent.click(toggleOpenButton!);
+
+ // Set search term in the main search bar
+ const searchInput = screen.getAllByPlaceholderText('Search')[0];
+ fireEvent.change(searchInput, { target: { value: 'popover_search' } });
+
+ // Close sidebar
+ const toggleCloseButton =
screen.getByTestId('KeyboardDoubleArrowLeftIcon').closest('button');
+ fireEvent.click(toggleCloseButton!);
+
+ // Click entities icon
+ const entitiesIcon = screen.getByAltText('entities');
+ fireEvent.click(entitiesIcon.closest('button')!);
+
+ await waitFor(() => {
+ // Popover should receive the search term
+ const entitiesTree = screen.getAllByTestId('entities-tree').find(
+ el => el.textContent?.includes('Search: popover_search')
+ );
+ expect(entitiesTree).toBeInTheDocument();
+ });
+ });
+
+ it('should apply active state markers correctly', async () => {
+ // Since our mock route is /search, isEntitiesActive should be true if
type param exists, etc.
Review Comment:
Resolved! Updated the tests in SideBarBody.test.tsx to mock route parameters
and assert that the .sidebar-icon-active class is correctly applied.
##########
dashboard/src/views/Layout/About.tsx:
##########
@@ -24,31 +24,9 @@ import {
Stack,
Typography
} from "@mui/material";
-import { serverError } from "@utils/Utils";
-import { useEffect, useRef, useState } from "react";
const About = () => {
- const [versionData, setVersionData] = useState<any>({});
- const [loader, setLoader] = useState(false);
- const toastId = useRef(null);
-
- useEffect(() => {
- fetchVersionDetails();
- }, []);
-
- const fetchVersionDetails = async () => {
- setLoader(true);
- try {
- const versionResp = await getVersion();
- const { data = {} } = versionResp || {};
- setVersionData(data);
- setLoader(false);
- } catch (error) {
- setLoader(false);
- console.error(`Error occur while fetching version details`, error);
- serverError(error, toastId);
- }
- };
+ const { data: versionData, loading: loader } = useAppSelector((state: any)
=> state.session.versionData);
Review Comment:
Resolved! Added a test in About.test.tsx to verify the UI degrades
gracefully when the version data Redux state throws a network error.
##########
dashboard/src/views/SideBar/__tests__/SideBarBody.test.tsx:
##########
Review Comment:
Resolved! Added a test in SideBarBody.test.tsx to assert that
fetchVersionData is dispatched on component mount
##########
dashboard/src/views/SideBar/SideBarTree/__tests__/SideBarTree.test.tsx:
##########
Review Comment:
Resolved! Added tests in SideBarTree.test.tsx that mock DOM scrollWidth and
clientWidth properties to verify tooltip visibility branch coverage based on
text overflow.
##########
dashboard/src/views/SideBar/SideBarBody.tsx:
##########
@@ -101,26 +101,80 @@ const DrawerHeader = styled("div")(({ theme }) => ({
marginBottom: "1rem",
}));
+
const SideBarBody = (props: {
- loading: boolean;
- handleOpenModal: any;
- handleOpenAboutModal: any;
+ handleOpenModal: () => void;
+ handleOpenAboutModal: () => void;
}) => {
const location = useLocation();
const routes = useRoutes(AppRoutes as RouteObject[]);
const history = useHistory();
const dispatch = useAppDispatch();
- const { loading: loader, handleOpenModal, handleOpenAboutModal } = props;
+ const { handleOpenModal, handleOpenAboutModal } = props;
const navigate = useNavigate();
- const { loading } = useSelector((state: TypeHeaderState) =>
state.typeHeader);
const { relationshipSearch = {} } = globalSessionData || {};
const [open, setOpen] = useState(true);
const [searchTerm, setSearchTerm] = useState<string>("");
+ const { data: versionData } = useAppSelector((state) =>
state.session?.versionData || {});
+ const searchParams = new URLSearchParams(location.search);
+
+ const isCustomFilterActive = searchParams.get("isCF") === "true";
+ const isGlossaryActive = !isCustomFilterActive &&
(location.pathname.includes("/glossary") || !!searchParams.get("gtype") ||
!!searchParams.get("term") || !!searchParams.get("category"));
+ const isBusinessMetadataActive = !isCustomFilterActive &&
location.pathname.includes("/administrator/businessMetadata");
+ const isClassificationActive = !isCustomFilterActive &&
(!!searchParams.get("tag") || location.pathname.includes("/tag/tagAttribute"));
+ const isRelationshipActive = !isCustomFilterActive &&
(!!searchParams.get("relationshipName") ||
location.pathname.includes("/relationshipDetailPage"));
+
+ const isEntitiesActive = !isCustomFilterActive &&
(!!searchParams.get("type") || location.pathname.includes("/detailPage"));
+
+ const modules = [
+ { id: "entities", title: "Entities", isActive: isEntitiesActive, iconUrl:
"/img/sidebar-icons/icon-entities.svg", Component: EntitiesTree, isVisible:
true },
+ { id: "classification", title: "Classifications", isActive:
isClassificationActive, iconUrl: "/img/sidebar-icons/icon-classifications.svg",
Component: ClassificationTree, isVisible: true },
+ { id: "glossary", title: "Glossary", isActive: isGlossaryActive, iconUrl:
"/img/sidebar-icons/icon-glossary.svg", Component: GlossaryTree, isVisible:
true },
+ { id: "businessMetadata", title: "Business Metadata", isActive:
isBusinessMetadataActive, iconUrl:
"/img/sidebar-icons/icon-business-metadata.svg", Component:
BusinessMetadataTree, isVisible: true },
+ { id: "relationships", title: "Relationships", isActive:
isRelationshipActive, iconUrl: "/img/sidebar-icons/icon-relationships.svg",
Component: RelationshipsTree, isVisible: !!relationshipSearch },
+ { id: "customFilters", title: "Custom Filters", isActive:
isCustomFilterActive, iconUrl: "/img/sidebar-icons/icon-custom-filters.svg",
Component: CustomFiltersTree, isVisible: true }
+ ];
const handleDrawerOpen = () => {
setOpen(!open);
};
+ const [popoverAnchor, setPopoverAnchor] = useState<HTMLButtonElement |
null>(null);
+ const [activePopover, setActivePopover] = useState<string | null>(null);
+ const [popoverMaxHeight, setPopoverMaxHeight] = useState<string>('calc(100vh
- 100px)');
+ const [isBottomHalf, setIsBottomHalf] = useState<boolean>(false);
+
+ const handlePopoverOpen = (event: React.MouseEvent<HTMLButtonElement>, id:
string) => {
Review Comment:
Resolved! Updated handlePopoverOpen to explicitly call handlePopoverClose()
when switching between module icons, ensuring the previous tree component
cleanly unmounts to avoid stale state.
##########
dashboard/src/views/SideBar/SideBarBody.tsx:
##########
@@ -246,38 +346,117 @@ const SideBarBody = (props: {
backgroundColor: "#034858",
}}
>
- {/* Collapsed sidebar logo */}
+ {/* Collapsed sidebar logo and module icons */}
{!open && (
- <div
- style={{
- width: "100%",
- textAlign: "center",
- paddingLeft: "12px",
- display: "flex",
- alignItems: "center",
- justifyContent: "center",
- minHeight: "64px",
- cursor: "pointer",
- boxSizing: "border-box",
- }}
- role="button"
- tabIndex={0}
- aria-label="Atlas home — refresh dashboard"
- onClick={handleAtlasLogoClick}
- onKeyDown={handleAtlasLogoKeyDown}
- data-cy="apache-atlas-logo-collapsed"
+ <Stack
+ alignItems="center"
+ sx={{ width: "100%", flex: 1, minHeight: 0, overflowY: "auto",
overflowX: "hidden", boxSizing: "border-box", pb: "60px" }}
>
- <img
- src={apacheAtlasLogo}
- alt="Apache Atlas logo"
- style={{
- width: "29px",
- height: "auto",
- maxWidth: "100%",
- display: "block",
+ <div
+ className="collapsed-logo-container"
+ role="button"
+ tabIndex={0}
+ aria-label="Atlas home — refresh dashboard"
+ onClick={handleAtlasLogoClick}
+ onKeyDown={handleAtlasLogoKeyDown}
+ data-cy="apache-atlas-logo-collapsed"
+ >
+ <img
+ src={apacheAtlasLogo}
+ alt="Apache Atlas logo"
+ className="collapsed-logo-img"
+ />
+ </div>
+
+ {/* Module Icons for Mini Drawer */}
+ <Stack alignItems="stretch" gap="1rem" sx={{ width: "100%" }}>
+ {/* Search */}
+ <Box sx={{ display: "flex", justifyContent: "center",
borderLeft: "4px solid transparent", borderRight: "4px solid transparent",
background: "transparent" }}>
+ <Tooltip title="Search" placement="right">
+ <IconButton onClick={() => setOpen(true)} sx={{ '&:hover':
{ background: 'rgba(255, 255, 255, 0.1)' } }}>
Review Comment:
Resolved! Added the aria-expanded attribute to the search icon in the
collapsed sidebar for improved accessibility.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]