From b3de772e22eeba51cc85cf21056dd48f78f7cc18 Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Wed, 8 Dec 2021 12:42:59 +0000 Subject: [PATCH] Create isLocationMatch function for location matching and setting active sidebar items Signed-off-by: hiba-aldalaty --- .../src/layout/Sidebar/Items.tsx | 11 +++--- .../src/layout/Sidebar/Sidebar.stories.tsx | 16 +------- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 11 +++--- .../src/layout/Sidebar/utils.ts | 38 +++++++++++++++++++ 4 files changed, 51 insertions(+), 25 deletions(-) create mode 100644 packages/core-components/src/layout/Sidebar/utils.ts diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index a41c3e4bdd..e1dcd4a15e 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -46,6 +46,7 @@ import { SidebarItemWithSubmenuContext, } from './config'; import { SidebarSubmenu } from './SidebarSubmenu'; +import { isLocationMatch } from './utils'; export type SidebarItemClassKey = | 'root' @@ -172,7 +173,7 @@ const useStyles = makeStyles( function isSidebarItemWithSubmenuActive( submenu: ReactNode, - locationPathname: string, + currentLocation: any, ) { // Item is active if any of submenu items have active paths const toPathnames: string[] = []; @@ -193,8 +194,8 @@ function isSidebarItemWithSubmenuActive( } }); isActive = toPathnames.some(to => { - const toPathname = resolvePath(to); - return locationPathname === toPathname.pathname; + const toLocation = resolvePath(to); + return isLocationMatch(currentLocation, toLocation); }); return isActive; } @@ -207,8 +208,8 @@ const SidebarItemWithSubmenu = ({ }: PropsWithChildren) => { const classes = useStyles(); const [isHoveredOn, setIsHoveredOn] = useState(false); - const { pathname: locationPathname } = useLocation(); - const isActive = isSidebarItemWithSubmenuActive(children, locationPathname); + const currentLocation = useLocation(); + const isActive = isSidebarItemWithSubmenuActive(children, currentLocation); const handleMouseEnter = () => { setIsHoveredOn(true); diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 5e68aa8d62..6afd0bf157 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -17,8 +17,6 @@ import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import BuildRoundedIcon from '@material-ui/icons/BuildRounded'; -import LibraryBooksOutlinedIcon from '@material-ui/icons/LibraryBooksOutlined'; -import WebOutlinedIcon from '@material-ui/icons/WebOutlined'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { @@ -34,7 +32,7 @@ import { import { SidebarSubmenuItem } from './SidebarSubmenuItem'; import MenuBookIcon from '@material-ui/icons/MenuBook'; import CloudQueueIcon from '@material-ui/icons/CloudQueue'; -import SettingsApplications from '@material-ui/icons/SettingsApplications'; +import AppsIcon from '@material-ui/icons/Apps'; import AcUnitIcon from '@material-ui/icons/AcUnit'; import { SidebarSubmenu } from './SidebarSubmenu'; @@ -76,17 +74,7 @@ export const SampleScalableSidebar = () => ( - - - + (theme => ({ item: { @@ -116,13 +117,13 @@ export type SidebarSubmenuItemProps = { export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { title, to, icon: Icon, dropdownItems } = props; const classes = useStyles(); - const { pathname: locationPathname, search: locationSearch } = useLocation(); - const { pathname: toPathname, search: toSearch } = useResolvedPath(to); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); const closeSubmenu = () => { setIsHoveredOn(false); }; - let isActive = locationPathname === toPathname && toSearch === locationSearch; + const toLocation = useResolvedPath(to); + const currentLocation = useLocation(); + let isActive = isLocationMatch(currentLocation, toLocation); const [showDropDown, setShowDropDown] = useState(false); const handleClickDropdown = () => { @@ -131,9 +132,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { if (dropdownItems !== undefined) { dropdownItems.some(item => { const resolvedPath = resolvePath(item.to); - isActive = - locationPathname === resolvedPath.pathname && - locationSearch === toSearch; + isActive = isLocationMatch(currentLocation, resolvedPath); return isActive; }); return ( diff --git a/packages/core-components/src/layout/Sidebar/utils.ts b/packages/core-components/src/layout/Sidebar/utils.ts new file mode 100644 index 0000000000..8fefb77bd9 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/utils.ts @@ -0,0 +1,38 @@ +/* + * Copyright 2020 The Backstage Authors + * + * Licensed 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 { Location, State, Path } from 'history'; +import { isEqual, isMatch } from 'lodash'; +import qs from 'qs'; + +export function isLocationMatch( + currentLocation: Location, + toLocation: Path, +) { + const toDecodedSearch = new URLSearchParams(toLocation.search).toString(); + const toQueryParameters = qs.parse(toDecodedSearch); + + const currentDecodedSearch = new URLSearchParams( + currentLocation.search, + ).toString(); + const currentQueryParameters = qs.parse(currentDecodedSearch); + + const matching = + isEqual(toLocation.pathname, currentLocation.pathname) && + isMatch(currentQueryParameters, toQueryParameters); + + return matching; +}