Merge pull request #17984 from chanzuckerberg/master
feat: allow making isLocationMatch use exact match in query string matcher
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
---
|
||||
|
||||
Added `exact` prop to `SidebarSubmenuItem` which causes it to only highlight if the current location is an exact match.
|
||||
@@ -1087,6 +1087,7 @@ export type SidebarSubmenuItemProps = {
|
||||
to?: string;
|
||||
icon?: IconComponent;
|
||||
dropdownItems?: SidebarSubmenuItemDropdownItem[];
|
||||
exact?: boolean;
|
||||
};
|
||||
|
||||
// @public
|
||||
|
||||
@@ -130,6 +130,7 @@ export type SidebarSubmenuItemProps = {
|
||||
to?: string;
|
||||
icon?: IconComponent;
|
||||
dropdownItems?: SidebarSubmenuItemDropdownItem[];
|
||||
exact?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -138,7 +139,7 @@ export type SidebarSubmenuItemProps = {
|
||||
* @public
|
||||
*/
|
||||
export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
const { title, subtitle, to, icon: Icon, dropdownItems } = props;
|
||||
const { title, subtitle, to, icon: Icon, dropdownItems, exact } = props;
|
||||
const classes = useStyles();
|
||||
const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext);
|
||||
const closeSubmenu = () => {
|
||||
@@ -146,7 +147,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
};
|
||||
const toLocation = useResolvedPath(to ?? '');
|
||||
const currentLocation = useLocation();
|
||||
let isActive = isLocationMatch(currentLocation, toLocation);
|
||||
let isActive = isLocationMatch(currentLocation, toLocation, exact);
|
||||
|
||||
const [showDropDown, setShowDropDown] = useState(false);
|
||||
const handleClickDropdown = () => {
|
||||
@@ -155,7 +156,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
|
||||
if (dropdownItems !== undefined) {
|
||||
dropdownItems.some(item => {
|
||||
const resolvedPath = resolvePath(item.to);
|
||||
isActive = isLocationMatch(currentLocation, resolvedPath);
|
||||
isActive = isLocationMatch(currentLocation, resolvedPath, exact);
|
||||
return isActive;
|
||||
});
|
||||
return (
|
||||
|
||||
@@ -95,4 +95,30 @@ describe('isLocationMatching', () => {
|
||||
toLocation = { pathname: '/catalog', search: '', hash: '' };
|
||||
expect(isLocationMatch(currentLocation, toLocation)).toBe(true);
|
||||
});
|
||||
|
||||
describe('exact matching', () => {
|
||||
it('return false when target query parameters are subset of current location query parameters', async () => {
|
||||
currentLocation = {
|
||||
pathname: '/catalog',
|
||||
search: '?x=foo&y=bar',
|
||||
state: null,
|
||||
hash: '',
|
||||
key: '',
|
||||
};
|
||||
toLocation = { pathname: '/catalog', search: '?x=foo', hash: '' };
|
||||
expect(isLocationMatch(currentLocation, toLocation, true)).toBe(false);
|
||||
});
|
||||
|
||||
it('return true when target query parameters are exact match with current location query parameters', async () => {
|
||||
currentLocation = {
|
||||
pathname: '/catalog',
|
||||
search: '?x=foo&y=bar',
|
||||
state: null,
|
||||
hash: '',
|
||||
key: '',
|
||||
};
|
||||
toLocation = { pathname: '/catalog', search: '?y=bar&x=foo', hash: '' };
|
||||
expect(isLocationMatch(currentLocation, toLocation, true)).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,11 @@ import { Location, Path } from 'history';
|
||||
import { isEqual, isMatch } from 'lodash';
|
||||
import qs from 'qs';
|
||||
|
||||
export function isLocationMatch(currentLocation: Location, toLocation: Path) {
|
||||
export function isLocationMatch(
|
||||
currentLocation: Location,
|
||||
toLocation: Path,
|
||||
exact: boolean = false,
|
||||
) {
|
||||
const toDecodedSearch = new URLSearchParams(toLocation.search).toString();
|
||||
const toQueryParameters = qs.parse(toDecodedSearch);
|
||||
|
||||
@@ -27,9 +31,11 @@ export function isLocationMatch(currentLocation: Location, toLocation: Path) {
|
||||
).toString();
|
||||
const currentQueryParameters = qs.parse(currentDecodedSearch);
|
||||
|
||||
const queryStringMatcher = exact ? isEqual : isMatch;
|
||||
|
||||
const matching =
|
||||
isEqual(toLocation.pathname, currentLocation.pathname) &&
|
||||
isMatch(currentQueryParameters, toQueryParameters);
|
||||
queryStringMatcher(currentQueryParameters, toQueryParameters);
|
||||
|
||||
return matching;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user