From 689840dcbe028b23748ec1c04a9a6f0e50bfacf7 Mon Sep 17 00:00:00 2001 From: Jonathan Ash Date: Wed, 16 Feb 2022 10:52:17 +0000 Subject: [PATCH 1/4] Added the ability for SidebarSubmenuItem to use external links via "to" property Signed-off-by: Jonathan Ash --- .changeset/unlucky-queens-brush.md | 5 ++++ .../src/layout/Sidebar/Bar.test.tsx | 26 +++++++++++++++++++ .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 7 +++-- .../src/layout/Sidebar/utils.test.ts | 14 +++++++++- .../src/layout/Sidebar/utils.ts | 2 ++ 5 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 .changeset/unlucky-queens-brush.md diff --git a/.changeset/unlucky-queens-brush.md b/.changeset/unlucky-queens-brush.md new file mode 100644 index 0000000000..c080856246 --- /dev/null +++ b/.changeset/unlucky-queens-brush.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-components': patch +--- + +Added ability for SidebarSubmenuItem to handle external links correctly via the "to" prop diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index 89133997f9..fc42e7a94e 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -46,6 +46,11 @@ async function renderScalableSidebar() { {}} text="Catalog"> + @@ -112,5 +121,22 @@ describe('Sidebar', () => { '/dropdownitemlink', ); }); + + it('Submenu item renders an external link when `to` value is provided', async () => { + userEvent.hover(screen.getByTestId('item-with-submenu')); + expect(screen.getByText('External Link').closest('a')).toHaveAttribute( + 'href', + 'https://backstage.io/', + ); + }); + + it('Dropdown item in submenu renders an external link when `to` value is provided', async () => { + userEvent.hover(screen.getByTestId('item-with-submenu')); + userEvent.click(screen.getByText('Misc')); + expect(screen.getByText('dropdown item 3').closest('a')).toHaveAttribute( + 'href', + 'https://backstage.io/', + ); + }); }); }); diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 1ede5f887e..1d697ead14 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -30,6 +30,8 @@ import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; import { isLocationMatch } from './utils'; +import { Link as BackstageLink } from '../../'; +import { isExternalLink } from './utils'; const useStyles = makeStyles(theme => ({ item: { @@ -133,6 +135,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const handleClickDropdown = () => { setShowDropDown(!showDropDown); }; + if (dropdownItems !== undefined) { dropdownItems.some(item => { const resolvedPath = resolvePath(item.to); @@ -163,7 +166,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
{dropdownItems.map((object, key) => ( { return (
{ let currentLocation: Location; @@ -96,3 +96,15 @@ describe('isLocationMatching', () => { expect(isLocationMatch(currentLocation, toLocation)).toBe(true); }); }); + +describe('isExternalLink', () => { + beforeEach(() => expect.hasAssertions()); + + it('should return true if provided with an external link', () => { + expect(isExternalLink('https://backstage.io/')).toEqual(true); + }); + + it('should return false if provided with a relative link', () => { + expect(isExternalLink('catalog')).toEqual(false); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/utils.ts b/packages/core-components/src/layout/Sidebar/utils.ts index 1afebe796d..7c85110aca 100644 --- a/packages/core-components/src/layout/Sidebar/utils.ts +++ b/packages/core-components/src/layout/Sidebar/utils.ts @@ -33,3 +33,5 @@ export function isLocationMatch(currentLocation: Location, toLocation: Path) { return matching; } + +export const isExternalLink = (link: string) => /^http/.test(link); From e01cc158c7ddb397f90c7274b3740d4696e12668 Mon Sep 17 00:00:00 2001 From: Jonathan Ash Date: Thu, 24 Feb 2022 09:07:07 +0000 Subject: [PATCH 2/4] Fixes for SidebarSubmenuItem to use external links Signed-off-by: Jonathan Ash --- .../core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx | 2 +- packages/core-components/src/layout/Sidebar/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 1d697ead14..a0ecc75918 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -30,7 +30,7 @@ import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; import { isLocationMatch } from './utils'; -import { Link as BackstageLink } from '../../'; +import { Link as BackstageLink } from '../../components/Link'; import { isExternalLink } from './utils'; const useStyles = makeStyles(theme => ({ diff --git a/packages/core-components/src/layout/Sidebar/utils.ts b/packages/core-components/src/layout/Sidebar/utils.ts index 7c85110aca..0c586ee3b3 100644 --- a/packages/core-components/src/layout/Sidebar/utils.ts +++ b/packages/core-components/src/layout/Sidebar/utils.ts @@ -34,4 +34,4 @@ export function isLocationMatch(currentLocation: Location, toLocation: Path) { return matching; } -export const isExternalLink = (link: string) => /^http/.test(link); +export const isExternalLink = (link: string) => /^https?:/.test(link); From 45837728045b6ac651a6211d63b276cfdcddb449 Mon Sep 17 00:00:00 2001 From: Jonathan Ash Date: Fri, 25 Feb 2022 10:16:28 +0000 Subject: [PATCH 3/4] - Switched out MUI with Backstage to handle external links Signed-off-by: Jonathan Ash --- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 14 ++------------ .../src/layout/Sidebar/utils.test.ts | 12 ------------ .../core-components/src/layout/Sidebar/utils.ts | 2 -- 3 files changed, 2 insertions(+), 26 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index a0ecc75918..d204728bcf 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -14,15 +14,10 @@ * limitations under the License. */ import React, { useContext, useState } from 'react'; -import { - NavLink, - resolvePath, - useLocation, - useResolvedPath, -} from 'react-router-dom'; +import { resolvePath, useLocation, useResolvedPath } from 'react-router-dom'; import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; -import Link from '@material-ui/core/Link'; +import { Link } from '../../components/Link'; import { IconComponent } from '@backstage/core-plugin-api'; import classnames from 'classnames'; import { BackstageTheme } from '@backstage/theme'; @@ -30,8 +25,6 @@ import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; import { isLocationMatch } from './utils'; -import { Link as BackstageLink } from '../../components/Link'; -import { isExternalLink } from './utils'; const useStyles = makeStyles(theme => ({ item: { @@ -135,7 +128,6 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const handleClickDropdown = () => { setShowDropDown(!showDropDown); }; - if (dropdownItems !== undefined) { dropdownItems.some(item => { const resolvedPath = resolvePath(item.to); @@ -166,7 +158,6 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
{dropdownItems.map((object, key) => ( { return (
{ expect(isLocationMatch(currentLocation, toLocation)).toBe(true); }); }); - -describe('isExternalLink', () => { - beforeEach(() => expect.hasAssertions()); - - it('should return true if provided with an external link', () => { - expect(isExternalLink('https://backstage.io/')).toEqual(true); - }); - - it('should return false if provided with a relative link', () => { - expect(isExternalLink('catalog')).toEqual(false); - }); -}); diff --git a/packages/core-components/src/layout/Sidebar/utils.ts b/packages/core-components/src/layout/Sidebar/utils.ts index 0c586ee3b3..1afebe796d 100644 --- a/packages/core-components/src/layout/Sidebar/utils.ts +++ b/packages/core-components/src/layout/Sidebar/utils.ts @@ -33,5 +33,3 @@ export function isLocationMatch(currentLocation: Location, toLocation: Path) { return matching; } - -export const isExternalLink = (link: string) => /^https?:/.test(link); From 94004f306662b0ccb8335af8bbc6a5f1b5fe1b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 25 Feb 2022 13:58:30 +0100 Subject: [PATCH 4/4] fixup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/core-components/src/layout/Sidebar/utils.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/src/layout/Sidebar/utils.test.ts b/packages/core-components/src/layout/Sidebar/utils.test.ts index 645177577f..b08dcda0fc 100644 --- a/packages/core-components/src/layout/Sidebar/utils.test.ts +++ b/packages/core-components/src/layout/Sidebar/utils.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ import { Location, Path } from 'history'; -import { isExternalLink, isLocationMatch } from './utils'; +import { isLocationMatch } from './utils'; describe('isLocationMatching', () => { let currentLocation: Location;