From b7c1a5a040bb2151aea39b68e9815d67bcb46e7f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 11 Aug 2021 20:11:22 +0200 Subject: [PATCH 001/116] Add SidebarGroup & BottomNavigation components Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 7 +++ .../src/layout/Sidebar/Bar.tsx | 22 ++++---- .../src/layout/Sidebar/BottomNavigation.tsx | 52 +++++++++++++++++++ .../src/layout/Sidebar/Group.tsx | 28 ++++++++++ .../src/layout/Sidebar/index.ts | 3 +- 5 files changed, 98 insertions(+), 14 deletions(-) create mode 100644 packages/core-components/src/layout/Sidebar/BottomNavigation.tsx create mode 100644 packages/core-components/src/layout/Sidebar/Group.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 7a666a020a..a8ce1451da 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -40,6 +40,8 @@ import { SidebarDivider, SidebarSpace, SidebarScrollWrapper, + BottomNavigation, + SidebarGroup, } from '@backstage/core-components'; const useSidebarLogoStyles = makeStyles({ @@ -101,6 +103,11 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + + + {children} ); diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index e6fa3820ad..9ee63c1c3d 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -133,20 +133,17 @@ export function Sidebar(props: PropsWithChildren) { const isOpen = (state === State.Open && !isSmallScreen) || isPinned; return ( -
-
) { {children}
-
); } diff --git a/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx b/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx new file mode 100644 index 0000000000..2356cab249 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx @@ -0,0 +1,52 @@ +/* + * 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 { + BottomNavigation as MUIBottomNavigation, + makeStyles, +} from '@material-ui/core'; +import React from 'react'; + +const useStyles = makeStyles({ + root: { + position: 'fixed', + bottom: 0, + left: 0, + right: 0, + }, +}); + +/** + * Filters for sidebar groups and reorders them to create a custom BottomNavigation + */ +export const BottomNavigation = ({ children }: React.PropsWithChildren<{}>) => { + const classes = useStyles(); + const [value, setValue] = React.useState('recents'); + + const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { + setValue(newValue); + }; + + return ( + + {children} + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Group.tsx b/packages/core-components/src/layout/Sidebar/Group.tsx new file mode 100644 index 0000000000..f346daa9a1 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/Group.tsx @@ -0,0 +1,28 @@ +/* + * 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 { BottomNavigationAction } from '@material-ui/core'; +import MenuIcon from '@material-ui/icons/Menu'; +import React from 'react'; + +/** + * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment + * Links to page, which will be displayed + * - If 'to' Prop is not defined it will render a Menu page out of the children (if children given) + */ +export const SidebarGroup = ({ children }: React.PropsWithChildren<{}>) => { + return } />; +}; diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 067a9bf2eb..cc1d127787 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,7 +15,8 @@ */ export { Sidebar } from './Bar'; -export type { SidebarClassKey } from './Bar'; +export { BottomNavigation } from './BottomNavigation'; +export { SidebarGroup } from './Group'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; export { From 772995ac5e3199901fcd9625f6e8f5afcbb68b7d Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 16 Aug 2021 11:23:56 +0200 Subject: [PATCH 002/116] Implement a grouping for mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 140 ++++++++++-------- .../src/layout/Sidebar/BottomNavigation.tsx | 52 ------- .../src/layout/Sidebar/Group.tsx | 43 +++++- .../src/layout/Sidebar/MobileSidebar.tsx | 96 ++++++++++++ .../src/layout/Sidebar/Page.tsx | 18 +-- .../src/layout/Sidebar/config.ts | 2 +- .../src/layout/Sidebar/index.ts | 2 +- 7 files changed, 224 insertions(+), 129 deletions(-) delete mode 100644 packages/core-components/src/layout/Sidebar/BottomNavigation.tsx create mode 100644 packages/core-components/src/layout/Sidebar/MobileSidebar.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index a8ce1451da..865e5e7e61 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -14,35 +14,44 @@ * limitations under the License. */ -import React, { useContext, PropsWithChildren } from 'react'; -import { Link, makeStyles } from '@material-ui/core'; -import HomeIcon from '@material-ui/icons/Home'; -import ExtensionIcon from '@material-ui/icons/Extension'; -import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; -import MapIcon from '@material-ui/icons/MyLocation'; -import LayersIcon from '@material-ui/icons/Layers'; -import LibraryBooks from '@material-ui/icons/LibraryBooks'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import MoneyIcon from '@material-ui/icons/MonetizationOn'; -import LogoFull from './LogoFull'; -import LogoIcon from './LogoIcon'; -import { NavLink } from 'react-router-dom'; +import { + MobileSidebar, + sidebarConfig, + SidebarContext, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, +} from '@backstage/core-components'; import { GraphiQLIcon } from '@backstage/plugin-graphiql'; -import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; import { SidebarSearch } from '@backstage/plugin-search'; import { Shortcuts } from '@backstage/plugin-shortcuts'; import { - Sidebar, - SidebarPage, - sidebarConfig, - SidebarContext, - SidebarItem, - SidebarDivider, - SidebarSpace, - SidebarScrollWrapper, + Settings as SidebarSettings, + UserSettingsSignInAvatar, +} from '@backstage/plugin-user-settings'; +import { BottomNavigation, - SidebarGroup, -} from '@backstage/core-components'; + BottomNavigationAction, + Link, + makeStyles, +} from '@material-ui/core'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import HomeIcon from '@material-ui/icons/Home'; +import LayersIcon from '@material-ui/icons/Layers'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import MenuIcon from '@material-ui/icons/Menu'; +import MoneyIcon from '@material-ui/icons/MonetizationOn'; +import MapIcon from '@material-ui/icons/MyLocation'; +import SearchIcon from '@material-ui/icons/Search'; +import React, { PropsWithChildren, useContext, useState } from 'react'; +import { NavLink } from 'react-router-dom'; +import LogoFull from './LogoFull'; +import LogoIcon from './LogoIcon'; const useSidebarLogoStyles = makeStyles({ root: { @@ -77,37 +86,52 @@ const SidebarLogo = () => { ); }; -export const Root = ({ children }: PropsWithChildren<{}>) => ( - - - - - - {/* Global nav, not org-specific */} - - - - - - {/* End global nav */} - - - - - - - - - - - - - - - - - - - {children} - -); +export const Root = ({ children }: PropsWithChildren<{}>) => { + return ( + + + + } to="/search"> + + + + {/* Global nav, not org-specific */} + }> + + + + + + {/* End global nav */} + + + + + + + + + + + + + } + to="/settings" + > + + + + {children} + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx b/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx deleted file mode 100644 index 2356cab249..0000000000 --- a/packages/core-components/src/layout/Sidebar/BottomNavigation.tsx +++ /dev/null @@ -1,52 +0,0 @@ -/* - * 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 { - BottomNavigation as MUIBottomNavigation, - makeStyles, -} from '@material-ui/core'; -import React from 'react'; - -const useStyles = makeStyles({ - root: { - position: 'fixed', - bottom: 0, - left: 0, - right: 0, - }, -}); - -/** - * Filters for sidebar groups and reorders them to create a custom BottomNavigation - */ -export const BottomNavigation = ({ children }: React.PropsWithChildren<{}>) => { - const classes = useStyles(); - const [value, setValue] = React.useState('recents'); - - const handleChange = (event: React.ChangeEvent<{}>, newValue: string) => { - setValue(newValue); - }; - - return ( - - {children} - - ); -}; diff --git a/packages/core-components/src/layout/Sidebar/Group.tsx b/packages/core-components/src/layout/Sidebar/Group.tsx index f346daa9a1..2aabea69b8 100644 --- a/packages/core-components/src/layout/Sidebar/Group.tsx +++ b/packages/core-components/src/layout/Sidebar/Group.tsx @@ -14,15 +14,50 @@ * limitations under the License. */ -import { BottomNavigationAction } from '@material-ui/core'; -import MenuIcon from '@material-ui/icons/Menu'; +import { + BottomNavigationAction, + BottomNavigationActionProps, + makeStyles, +} from '@material-ui/core'; import React from 'react'; +import { NavLink, useLocation } from 'react-router-dom'; + +interface SidebarGroupProps extends BottomNavigationActionProps { + to?: string; + injectedSelected?: boolean; +} + +const useStyles = makeStyles({ + label: { + display: 'none', + }, +}); /** * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment * Links to page, which will be displayed * - If 'to' Prop is not defined it will render a Menu page out of the children (if children given) */ -export const SidebarGroup = ({ children }: React.PropsWithChildren<{}>) => { - return } />; +export const SidebarGroup = ({ + to, + label, + icon, + onClick, + injectedSelected, +}: React.PropsWithChildren) => { + const classes = useStyles(); + const location = useLocation(); + + return ( + + ); }; diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx new file mode 100644 index 0000000000..4d846e3580 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -0,0 +1,96 @@ +/* + * 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 { BottomNavigation, Box, makeStyles } from '@material-ui/core'; +import React, { useState, useEffect } from 'react'; +import { useLocation } from 'react-router'; +import { SidebarGroup } from './Group'; + +const useStyles = makeStyles({ + root: { + position: 'fixed', + bottom: 0, + left: 0, + right: 0, + zIndex: 1000, + }, +}); + +const OverlayMenu = ({ children }: React.PropsWithChildren<{}>) => ( + + {children} + +); + +let currentLocation: string; + +/** + * Filters for sidebar groups and reorders them to create a custom BottomNavigation + */ +export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { + const classes = useStyles(); + const [value, setValue] = useState(); + const [menu, setMenu] = useState(); + const location = useLocation(); + + useEffect(() => { + if (menu && currentLocation !== location.pathname) { + setMenu(undefined); + setValue(undefined); + } + currentLocation = location.pathname; + }, [location, menu]); + + return ( + + {value && menu && } + + {React.Children.map(children, (child, index) => { + if ( + React.isValidElement(child) && + (child as React.ReactElement).type === SidebarGroup + ) { + if (index === value && !menu && !child.props.to) { + setMenu(child.props.children); + } + return React.cloneElement(child, { + injectedSelected: value + ? index === value + : location.pathname === child.props.to, + onClick: () => { + if (index === value && menu) { + setValue(undefined); + setMenu(undefined); + } else { + setValue(index); + } + }, + }); + } + return null; + })} + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index cc50c5af6b..d33564b5d4 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -25,19 +25,11 @@ import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; -export type SidebarPageClassKey = 'root'; - -const useStyles = makeStyles( - { - root: { - width: '100%', - minHeight: '100%', - transition: 'padding-left 0.1s ease-out', - paddingLeft: ({ isPinned }) => - isPinned - ? sidebarConfig.drawerWidthOpen - : sidebarConfig.drawerWidthClosed, - }, +const useStyles = makeStyles({ + root: { + width: '100%', + minHeight: '100%', + transition: 'padding-left 0.1s ease-out', }, { name: 'BackstageSidebarPage' }, ); diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index d2a690e38f..bfff039492 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -45,5 +45,5 @@ export type SidebarContextType = { }; export const SidebarContext = createContext({ - isOpen: false, + isOpen: true, }); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index cc1d127787..320625dc11 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,7 +15,7 @@ */ export { Sidebar } from './Bar'; -export { BottomNavigation } from './BottomNavigation'; +export { MobileSidebar } from './MobileSidebar'; export { SidebarGroup } from './Group'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; From cbebaa841bd6bce99011cd17469205af58815a90 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 18 Aug 2021 10:38:55 +0200 Subject: [PATCH 003/116] Refactored select behaviour to be better readable - Remove header from Search & Settings on mobile Co-authored-by: Tejas Kumar Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 9 +- .../app/src/components/search/SearchPage.tsx | 65 +++++--- .../src/layout/Sidebar/MobileSidebar.tsx | 155 +++++++++++------- .../Sidebar/{Group.tsx => SidebarGroup.tsx} | 47 ++++-- .../src/layout/Sidebar/config.ts | 1 + .../src/layout/Sidebar/index.ts | 2 +- .../General/UserSettingsGeneral.tsx | 4 +- .../src/components/SettingsPage.tsx | 11 +- 8 files changed, 192 insertions(+), 102 deletions(-) rename packages/core-components/src/layout/Sidebar/{Group.tsx => SidebarGroup.tsx} (54%) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 865e5e7e61..fa7fc892b2 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -32,12 +32,7 @@ import { Settings as SidebarSettings, UserSettingsSignInAvatar, } from '@backstage/plugin-user-settings'; -import { - BottomNavigation, - BottomNavigationAction, - Link, - makeStyles, -} from '@material-ui/core'; +import { Link, makeStyles } from '@material-ui/core'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import RuleIcon from '@material-ui/icons/AssignmentTurnedIn'; import ExtensionIcon from '@material-ui/icons/Extension'; @@ -48,7 +43,7 @@ import MenuIcon from '@material-ui/icons/Menu'; import MoneyIcon from '@material-ui/icons/MonetizationOn'; import MapIcon from '@material-ui/icons/MyLocation'; import SearchIcon from '@material-ui/icons/Search'; -import React, { PropsWithChildren, useContext, useState } from 'react'; +import React, { PropsWithChildren, useContext } from 'react'; import { NavLink } from 'react-router-dom'; import LogoFull from './LogoFull'; import LogoIcon from './LogoIcon'; diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 54f59d1a6e..7829fa66aa 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -25,8 +25,18 @@ import { SearchType, } from '@backstage/plugin-search'; import { DocsResultListItem } from '@backstage/plugin-techdocs'; -import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core'; -import React from 'react'; +import { SearchResultSet } from '@backstage/search-common'; +import { BackstageTheme } from '@backstage/theme'; +import { + Grid, + List, + makeStyles, + Paper, + Theme, + useMediaQuery, +} from '@material-ui/core'; +import Pagination from '@material-ui/lab/Pagination'; +import React, { useState } from 'react'; const useStyles = makeStyles((theme: Theme) => ({ bar: { @@ -44,9 +54,16 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); + // Think about abstraction to utils here + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.up('xs'), + ); + return ( -
} /> + {!isMobileScreen && ( +
} /> + )} @@ -54,26 +71,28 @@ const SearchPage = () => { - - - - - - - - + {!isMobileScreen && ( + + + + + + + + )} + {({ results }) => ( diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 4d846e3580..d3cd9e65ae 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -14,83 +14,128 @@ * limitations under the License. */ -import { BottomNavigation, Box, makeStyles } from '@material-ui/core'; -import React, { useState, useEffect } from 'react'; +import { BackstageTheme } from '@backstage/theme'; +import { + BottomNavigation, + Box, + IconButton, + makeStyles, + Typography, +} from '@material-ui/core'; +import React, { createContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router'; -import { SidebarGroup } from './Group'; +import { sidebarConfig } from './config'; +import { SidebarGroup } from './SidebarGroup'; +import CloseIcon from '@material-ui/icons/Close'; -const useStyles = makeStyles({ +type MobileSidebarContextType = { + selectedMenuItemIndex: number; + setSelectedMenuItemIndex: React.Dispatch>; +}; + +const useStyles = makeStyles(theme => ({ root: { position: 'fixed', + backgroundColor: theme.palette.navigation.background, + color: theme.palette.navigation.color, bottom: 0, left: 0, right: 0, zIndex: 1000, + borderTop: '1px solid #383838', }, + + overlay: { + background: theme.palette.navigation.background, + width: '100%', + flex: '0 1 auto', + height: `calc(100% - ${sidebarConfig.mobileSidebarHeight}px)`, + overflow: 'auto', + position: 'fixed', + zIndex: 500, + }, + + overlayHeader: { + display: 'flex', + color: theme.palette.bursts.fontColor, + alignItems: 'center', + justifyContent: 'space-between', + padding: `${theme.spacing(2)}px ${theme.spacing(3)}px`, + }, + + overlayHeaderClose: { + color: theme.palette.bursts.fontColor, + }, +})); + +const OverlayMenu = ({ + children, + label, + onClose, +}: React.PropsWithChildren<{ label: string; onClose: () => void }>) => { + const classes = useStyles(); + + return ( + + + {label} + + + + + {children} + + ); +}; + +export const MobileSidebarContext = createContext({ + selectedMenuItemIndex: -1, + setSelectedMenuItemIndex: () => {}, }); -const OverlayMenu = ({ children }: React.PropsWithChildren<{}>) => ( - - {children} - -); - -let currentLocation: string; - /** * Filters for sidebar groups and reorders them to create a custom BottomNavigation */ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { const classes = useStyles(); - const [value, setValue] = useState(); - const [menu, setMenu] = useState(); const location = useLocation(); + const [selectedMenuItemIndex, setSelectedMenuItemIndex] = + useState(-1); useEffect(() => { - if (menu && currentLocation !== location.pathname) { - setMenu(undefined); - setValue(undefined); - } - currentLocation = location.pathname; - }, [location, menu]); + // This is getting triggered to often - fix me! + setSelectedMenuItemIndex(-1); + }, [location.pathname]); + + const sidebarGroups = React.Children.map(children, child => + React.isValidElement(child) && child.type === SidebarGroup ? child : null, + ); + + if (!sidebarGroups) { + // error api + return null; // think about the exception state + } + + const shouldShowGroupChildren = + selectedMenuItemIndex >= 0 && + !sidebarGroups[selectedMenuItemIndex].props.to; return ( - - {value && menu && } + + {shouldShowGroupChildren && ( + setSelectedMenuItemIndex(-1)} + /> + )} - {React.Children.map(children, (child, index) => { - if ( - React.isValidElement(child) && - (child as React.ReactElement).type === SidebarGroup - ) { - if (index === value && !menu && !child.props.to) { - setMenu(child.props.children); - } - return React.cloneElement(child, { - injectedSelected: value - ? index === value - : location.pathname === child.props.to, - onClick: () => { - if (index === value && menu) { - setValue(undefined); - setMenu(undefined); - } else { - setValue(index); - } - }, - }); - } - return null; - })} + {sidebarGroups} - + ); }; diff --git a/packages/core-components/src/layout/Sidebar/Group.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx similarity index 54% rename from packages/core-components/src/layout/Sidebar/Group.tsx rename to packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 2aabea69b8..db3c39e227 100644 --- a/packages/core-components/src/layout/Sidebar/Group.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-shadow */ /* * Copyright 2020 The Backstage Authors * @@ -14,24 +15,34 @@ * limitations under the License. */ +import { BackstageTheme } from '@backstage/theme'; import { BottomNavigationAction, BottomNavigationActionProps, makeStyles, } from '@material-ui/core'; -import React from 'react'; -import { NavLink, useLocation } from 'react-router-dom'; +import React, { useContext } from 'react'; +import { useLocation } from 'react-router-dom'; +import { Link } from '../../components'; +import { MobileSidebarContext } from './MobileSidebar'; interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; - injectedSelected?: boolean; } -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ + root: { + color: theme.palette.navigation.color, + }, + + selected: { + color: `${theme.palette.navigation.selectedColor}!important`, + }, + label: { display: 'none', }, -}); +})); /** * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment @@ -42,21 +53,35 @@ export const SidebarGroup = ({ to, label, icon, - onClick, - injectedSelected, + value, }: React.PropsWithChildren) => { const classes = useStyles(); const location = useLocation(); + const { selectedMenuItemIndex, setSelectedMenuItemIndex } = + useContext(MobileSidebarContext); + + const onChange = (_, value) => { + if (value === selectedMenuItemIndex && to !== location.pathname) { + setSelectedMenuItemIndex(-1); + } else if (value !== selectedMenuItemIndex) { + setSelectedMenuItemIndex(value); + } + }; + + const selected = + value === selectedMenuItemIndex || + (selectedMenuItemIndex >= 0 && to === location.pathname); return ( + // @ts-ignore Material UI issue: https://github.com/mui-org/material-ui/issues/27820 ); diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index bfff039492..e213385f73 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -35,6 +35,7 @@ export const sidebarConfig = { selectedIndicatorWidth: 3, userBadgePadding, userBadgeDiameter: drawerWidthClosed - userBadgePadding * 2, + mobileSidebarHeight: 56, }; export const SIDEBAR_INTRO_LOCAL_STORAGE = diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 320625dc11..26a4c0e8a5 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -16,7 +16,7 @@ export { Sidebar } from './Bar'; export { MobileSidebar } from './MobileSidebar'; -export { SidebarGroup } from './Group'; +export { SidebarGroup } from './SidebarGroup'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; export { diff --git a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx index bd08fef565..6705997d1c 100644 --- a/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsGeneral.tsx @@ -21,10 +21,10 @@ import { UserSettingsAppearanceCard } from './UserSettingsAppearanceCard'; export const UserSettingsGeneral = () => { return ( - + - + diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index eebf6ff865..20ea6820f4 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -14,21 +14,26 @@ * limitations under the License. */ +import { Header, Page, TabbedLayout } from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; +import { useMediaQuery } from '@material-ui/core'; import React from 'react'; import { UserSettingsAuthProviders } from './AuthProviders'; import { UserSettingsFeatureFlags } from './FeatureFlags'; import { UserSettingsGeneral } from './General'; -import { Header, Page, TabbedLayout } from '@backstage/core-components'; type Props = { providerSettings?: JSX.Element; }; export const SettingsPage = ({ providerSettings }: Props) => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.up('xs'), + ); + return ( -
- + {!isMobileScreen &&
} From d5f08f4b03505702bdc291742a7026c437f05821 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 19 Aug 2021 12:33:34 +0200 Subject: [PATCH 004/116] Adjust display style & selected behaviour Signed-off-by: Philipp Hugenroth --- .../core-components/src/layout/Page/Page.tsx | 31 +++-- .../src/layout/Sidebar/Items.tsx | 106 +++++++++++++----- .../src/layout/Sidebar/SidebarGroup.tsx | 17 ++- .../CatalogResultListItem.tsx | 1 + .../General/UserSettingsAppearanceCard.tsx | 2 +- 5 files changed, 104 insertions(+), 53 deletions(-) diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 57653d23b5..ed59fc530d 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -16,24 +16,21 @@ import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; +import { makeStyles, ThemeProvider } from '@material-ui/core'; +import { sidebarConfig } from '../Sidebar'; -export type PageClassKey = 'root'; - -const useStyles = makeStyles( - () => ({ - root: { - display: 'grid', - gridTemplateAreas: - "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", - gridTemplateRows: 'max-content auto 1fr', - gridTemplateColumns: 'auto 1fr auto', - height: '100vh', - overflowY: 'auto', - }, - }), - { name: 'BackstagePage' }, -); +const useStyles = makeStyles({ + root: { + display: 'grid', + gridTemplateAreas: + "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", + gridTemplateRows: 'max-content auto 1fr', + gridTemplateColumns: 'auto 1fr auto', + // TODO: Only mobile + height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, + overflowY: 'auto', + }, +}); type Props = { themeId: string; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index ed089ad6a1..13602b83d0 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -38,36 +38,82 @@ import { } from 'react-router-dom'; import { sidebarConfig, SidebarContext } from './config'; -export type SidebarItemClassKey = - | 'root' - | 'buttonItem' - | 'closed' - | 'open' - | 'label' - | 'iconContainer' - | 'searchRoot' - | 'searchField' - | 'searchFieldHTMLInput' - | 'searchContainer' - | 'secondaryAction' - | 'selected'; -const useStyles = makeStyles( - theme => { - const { - selectedIndicatorWidth, - drawerWidthClosed, - drawerWidthOpen, - iconContainerWidth, - } = sidebarConfig; - return { - root: { - color: theme.palette.navigation.color, - display: 'flex', - flexFlow: 'row nowrap', - alignItems: 'center', - height: 48, - cursor: 'pointer', +const useStyles = makeStyles(theme => { + const { + selectedIndicatorWidth, + drawerWidthClosed, + drawerWidthOpen, + iconContainerWidth, + } = sidebarConfig; + return { + root: { + color: theme.palette.navigation.color, + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + height: 48, + cursor: 'pointer', + }, + buttonItem: { + background: 'none', + border: 'none', + width: 'auto', + margin: 0, + padding: 0, + textAlign: 'inherit', + font: 'inherit', + }, + closed: { + width: drawerWidthClosed, + justifyContent: 'center', + }, + open: { + // Does not apply on mobile + // width: drawerWidthOpen, + }, + label: { + // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs + fontWeight: 'bold', + whiteSpace: 'nowrap', + lineHeight: 'auto', + flex: '3 1 auto', + width: '110px', + overflow: 'hidden', + 'text-overflow': 'ellipsis', + }, + iconContainer: { + boxSizing: 'border-box', + height: '100%', + width: iconContainerWidth, + marginRight: -theme.spacing(2), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + searchRoot: { + marginBottom: 12, + }, + searchField: { + color: '#b5b5b5', + fontWeight: 'bold', + fontSize: theme.typography.fontSize, + }, + searchFieldHTMLInput: { + padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + }, + searchContainer: { + width: drawerWidthOpen - iconContainerWidth, + }, + secondaryAction: { + width: theme.spacing(6), + textAlign: 'center', + marginRight: theme.spacing(1), + }, + selected: { + '&$root': { + borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, + color: theme.palette.navigation.selectedColor, }, buttonItem: { background: 'none', @@ -136,7 +182,7 @@ const useStyles = makeStyles( }, }, }; - }, + }}, { name: 'BackstageSidebarItem' }, ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index db3c39e227..02267931dc 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -24,6 +24,7 @@ import { import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; import { Link } from '../../components'; +import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; interface SidebarGroupProps extends BottomNavigationActionProps { @@ -32,11 +33,15 @@ interface SidebarGroupProps extends BottomNavigationActionProps { const useStyles = makeStyles(theme => ({ root: { + flexGrow: 0, + margin: `0 ${theme.spacing(2)}px`, color: theme.palette.navigation.color, }, selected: { color: `${theme.palette.navigation.selectedColor}!important`, + borderTop: `solid ${sidebarConfig.selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, + marginTop: '-1px', }, label: { @@ -60,17 +65,19 @@ export const SidebarGroup = ({ const { selectedMenuItemIndex, setSelectedMenuItemIndex } = useContext(MobileSidebarContext); - const onChange = (_, value) => { - if (value === selectedMenuItemIndex && to !== location.pathname) { + const onChange = (_: React.ChangeEvent<{}>, value: number) => { + if (value === selectedMenuItemIndex) { setSelectedMenuItemIndex(-1); - } else if (value !== selectedMenuItemIndex) { + } else { setSelectedMenuItemIndex(value); } }; const selected = - value === selectedMenuItemIndex || - (selectedMenuItemIndex >= 0 && to === location.pathname); + (value === selectedMenuItemIndex && selectedMenuItemIndex >= 0) || + (!(value === selectedMenuItemIndex) && + !(selectedMenuItemIndex >= 0) && + to === location.pathname); return ( // @ts-ignore Material UI issue: https://github.com/mui-org/material-ui/issues/27820 diff --git a/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx b/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx index 1aaa50a7bf..f0a32ec0e2 100644 --- a/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx +++ b/plugins/catalog/src/components/CatalogResultListItem/CatalogResultListItem.tsx @@ -31,6 +31,7 @@ const useStyles = makeStyles({ }, itemText: { width: '100%', + wordBreak: 'break-all', marginBottom: '1rem', }, }); diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 8f6e60e289..cb198c8d01 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -23,7 +23,7 @@ export const UserSettingsAppearanceCard = () => ( - + {/* */} ); From 746af79164987d94d7ac891580fd974f5b379859 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 19 Aug 2021 13:56:04 +0200 Subject: [PATCH 005/116] Align to current screen size Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 6 +-- .../app/src/components/search/SearchPage.tsx | 3 +- .../core-components/src/layout/Page/Page.tsx | 12 ++++-- .../src/layout/Sidebar/Bar.tsx | 42 ++++++++++++------- .../src/layout/Sidebar/Items.tsx | 5 ++- .../src/layout/Sidebar/Page.tsx | 22 ++++++---- .../src/layout/Sidebar/SidebarGroup.tsx | 12 +++++- plugins/shortcuts/src/AddShortcut.tsx | 2 +- .../General/UserSettingsAppearanceCard.tsx | 27 +++++++----- .../src/components/SettingsPage.tsx | 2 +- 10 files changed, 86 insertions(+), 47 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index fa7fc892b2..b9b0384afb 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -15,7 +15,7 @@ */ import { - MobileSidebar, + Sidebar, sidebarConfig, SidebarContext, SidebarDivider, @@ -84,7 +84,7 @@ const SidebarLogo = () => { export const Root = ({ children }: PropsWithChildren<{}>) => { return ( - + } to="/search"> @@ -125,7 +125,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => { > - + {children} ); diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 7829fa66aa..fefed84f58 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -54,9 +54,8 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - // Think about abstraction to utils here const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.up('xs'), + theme.breakpoints.down('xs'), ); return ( diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index ed59fc530d..9f49174977 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -19,18 +19,22 @@ import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core'; import { sidebarConfig } from '../Sidebar'; -const useStyles = makeStyles({ +const useStyles = makeStyles(theme => ({ root: { display: 'grid', gridTemplateAreas: "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", gridTemplateRows: 'max-content auto 1fr', gridTemplateColumns: 'auto 1fr auto', - // TODO: Only mobile - height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, + [theme.breakpoints.up('sm')]: { + height: '100vh', + }, + [theme.breakpoints.down('xs')]: { + height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, + }, overflowY: 'auto', }, -}); +})); type Props = { themeId: string; diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 9ee63c1c3d..ead77d4cae 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -21,6 +21,7 @@ import React, { useRef, useState, useContext, PropsWithChildren } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; +import { MobileSidebar } from './MobileSidebar'; export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; @@ -80,12 +81,11 @@ type Props = { closeDelayMs?: number; }; -export function Sidebar(props: PropsWithChildren) { - const { - openDelayMs = sidebarConfig.defaultOpenDelayMs, - closeDelayMs = sidebarConfig.defaultCloseDelayMs, - children, - } = props; +const DesktopSidebar = ({ + openDelayMs = sidebarConfig.defaultOpenDelayMs, + closeDelayMs = sidebarConfig.defaultCloseDelayMs, + children, +}: PropsWithChildren) => { const classes = useStyles(); const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('md'), @@ -138,18 +138,30 @@ export function Sidebar(props: PropsWithChildren) { isOpen, }} > -
- {children} -
- + className={clsx(classes.drawer, { + [classes.drawerOpen]: isOpen, + })} + > + {children} + + ); -} +}; + +export const Sidebar = ({ children }: React.PropsWithChildren<{}>) => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + return isMobileScreen ? ( + {children} + ) : ( + {children} + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 13602b83d0..19fd8c0c0a 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -69,8 +69,9 @@ const useStyles = makeStyles(theme => { justifyContent: 'center', }, open: { - // Does not apply on mobile - // width: drawerWidthOpen, + [theme.breakpoints.up('sm')]: { + width: drawerWidthOpen, + }, }, label: { // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index d33564b5d4..cecc985a28 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core/styles'; +import { makeStyles } from '@material-ui/core'; import React, { createContext, PropsWithChildren, @@ -25,12 +25,20 @@ import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; -const useStyles = makeStyles({ - root: { - width: '100%', - minHeight: '100%', - transition: 'padding-left 0.1s ease-out', - }, +const useStyles = makeStyles( + theme => ({ + root: { + width: '100%', + minHeight: '100%', + transition: 'padding-left 0.1s ease-out', + [theme.breakpoints.up('sm')]: { + paddingLeft: ({ isPinned }) => + isPinned + ? sidebarConfig.drawerWidthOpen + : sidebarConfig.drawerWidthClosed, + }, + }, + }), { name: 'BackstageSidebarPage' }, ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 02267931dc..75dbb9ae91 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -20,6 +20,7 @@ import { BottomNavigationAction, BottomNavigationActionProps, makeStyles, + useMediaQuery, } from '@material-ui/core'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; @@ -59,11 +60,15 @@ export const SidebarGroup = ({ label, icon, value, + children, }: React.PropsWithChildren) => { const classes = useStyles(); const location = useLocation(); const { selectedMenuItemIndex, setSelectedMenuItemIndex } = useContext(MobileSidebarContext); + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); const onChange = (_: React.ChangeEvent<{}>, value: number) => { if (value === selectedMenuItemIndex) { @@ -79,8 +84,9 @@ export const SidebarGroup = ({ !(selectedMenuItemIndex >= 0) && to === location.pathname); - return ( - // @ts-ignore Material UI issue: https://github.com/mui-org/material-ui/issues/27820 + return isMobileScreen ? ( + // Material UI issue: https://github.com/mui-org/material-ui/issues/27820 + // @ts-ignore + ) : ( + <>{children} ); }; diff --git a/plugins/shortcuts/src/AddShortcut.tsx b/plugins/shortcuts/src/AddShortcut.tsx index 87c2750206..7c1c5ca118 100644 --- a/plugins/shortcuts/src/AddShortcut.tsx +++ b/plugins/shortcuts/src/AddShortcut.tsx @@ -31,7 +31,7 @@ import { alertApiRef, useApi } from '@backstage/core-plugin-api'; const useStyles = makeStyles(theme => ({ card: { - width: 400, + maxWidth: 400, }, header: { marginBottom: theme.spacing(1), diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index cb198c8d01..83937f9300 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,17 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; -import { List } from '@material-ui/core'; import { InfoCard } from '@backstage/core-components'; +import { BackstageTheme } from '@backstage/theme'; +import { List, useMediaQuery } from '@material-ui/core'; +import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; -export const UserSettingsAppearanceCard = () => ( - - - - {/* */} - - -); +export const UserSettingsAppearanceCard = () => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + return ( + + + + {!isMobileScreen && } + + + ); +}; diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index 20ea6820f4..d5c7171148 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -28,7 +28,7 @@ type Props = { export const SettingsPage = ({ providerSettings }: Props) => { const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.up('xs'), + theme.breakpoints.down('xs'), ); return ( From 65e1a35c0fa05c295f84bed6292d22f2e9103365 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 20 Aug 2021 11:00:21 +0200 Subject: [PATCH 006/116] Update documentation & add changeset Signed-off-by: Philipp Hugenroth --- .changeset/fuzzy-olives-impress.md | 8 ++ .../configure-app-with-plugins.md | 17 ++++ packages/app/src/components/Root/Root.tsx | 88 ++++++++----------- .../src/layout/Sidebar/MobileSidebar.tsx | 11 ++- .../src/layout/Sidebar/Sidebar.stories.tsx | 20 +++-- plugins/cost-insights/README.md | 24 +++-- 6 files changed, 100 insertions(+), 68 deletions(-) create mode 100644 .changeset/fuzzy-olives-impress.md diff --git a/.changeset/fuzzy-olives-impress.md b/.changeset/fuzzy-olives-impress.md new file mode 100644 index 0000000000..9f9c8f755b --- /dev/null +++ b/.changeset/fuzzy-olives-impress.md @@ -0,0 +1,8 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-catalog': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-user-settings': patch +--- + +Add `MobileSidebar` component & use it for smaller screens to improve the UX on mobile devices by switching to a navigation at the bottom of the screen. For customizing the experience you can group `SidebarItems` in a `SidebarGroup` or create a `SidebarGroup` with a link. For an example take a look at the `Root.tsx` or the updated ["Adding a plugin page to the Sidebar"](https://backstage.io/docs/getting-started/configure-app-with-plugins) documentation. diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 746b4bd940..11e2f4d75a 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -90,3 +90,20 @@ extension to `.icon.svg`. For example: ```ts import InternalToolIcon from './internal-tool.icon.svg'; ``` + +On mobile devices the `Sidebar` is displayed at the bottom of the screen. For +customizing the experience you can group `SidebarItems` in a `SidebarGroup` or +create a `SidebarGroup` with a link. All `SidebarGroups` are displayed in the +bottom navigation with an icon. + +```ts +// ... inside the AppSidebar component +} label="Menu"> + ... + + ... + +``` + +If no `SidebarGroup`is provided a default menu will display the `Sidebar` +content. diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index b9b0384afb..5995346a6f 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -81,52 +81,42 @@ const SidebarLogo = () => { ); }; -export const Root = ({ children }: PropsWithChildren<{}>) => { - return ( - - - - } to="/search"> - - - - {/* Global nav, not org-specific */} - }> - - - - - - {/* End global nav */} - - - - - - - - - - - - - } - to="/settings" - > - - - - {children} - - ); -}; +export const Root = ({ children }: PropsWithChildren<{}>) => ( + + + + {/* } to="/search"> */} + + {/* */} + + {/* Global nav, not org-specific */} + {/* }> */} + + + + + + {/* End global nav */} + + + + + + + + + + {/* */} + + + {/* } + to="/settings" + > */} + + {/* */} + + {children} + +); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index d3cd9e65ae..633f366501 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -22,11 +22,12 @@ import { makeStyles, Typography, } from '@material-ui/core'; +import CloseIcon from '@material-ui/icons/Close'; +import MenuIcon from '@material-ui/icons/Menu'; import React, { createContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router'; import { sidebarConfig } from './config'; import { SidebarGroup } from './SidebarGroup'; -import CloseIcon from '@material-ui/icons/Close'; type MobileSidebarContextType = { selectedMenuItemIndex: number; @@ -115,8 +116,14 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { ); if (!sidebarGroups) { - // error api return null; // think about the exception state + } else if (!sidebarGroups.length) { + // Render default SidebarGroup if no + sidebarGroups.push( + }> + {children} + , + ); } const shouldShowGroupChildren = diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index aee5c70b43..5aa279d846 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -16,11 +16,13 @@ import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; +import MenuIcon from '@material-ui/icons/Menu'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; import { Sidebar, SidebarDivider, + SidebarGroup, SidebarIntro, SidebarItem, SidebarSearchField, @@ -44,13 +46,15 @@ const handleSearch = (input: string) => { export const SampleSidebar = () => ( - - - - - - - - + + + + + + + + + + ); diff --git a/plugins/cost-insights/README.md b/plugins/cost-insights/README.md index 187ab78b73..91f717dfaa 100644 --- a/plugins/cost-insights/README.md +++ b/plugins/cost-insights/README.md @@ -79,21 +79,27 @@ To expose the plugin to your users, you can integrate the `cost-insights` route export const AppSidebar = () => ( - + } to="/search"> + + {/* Global nav, not org-specific */} - - - - - - -+ + }> + + + + + + + + + {/* End global nav */} - + } to="/settings"> + + ); ``` From 958e0b46479aa28f58497450a983868852637897 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 20 Aug 2021 11:28:47 +0200 Subject: [PATCH 007/116] Adjust create-app & api-report Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 54 ++++++++++--------- packages/core-components/api-report.md | 33 ++++++++++-- .../src/layout/Sidebar/Bar.tsx | 7 ++- .../packages/app/src/components/Root/Root.tsx | 53 ++++++++++-------- 4 files changed, 94 insertions(+), 53 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 5995346a6f..1851145a31 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -85,37 +85,41 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - {/* } to="/search"> */} - - {/* */} + } to="/search"> + + - {/* Global nav, not org-specific */} - {/* }> */} - - - - - - {/* End global nav */} - - - - - - - - - - {/* */} + }> + {/* Global nav, not org-specific */} + + + + + + {/* End global nav */} + + + + + + + + + + - {/* } to="/settings" - > */} - - {/* */} + > + + {children} diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index cc884c9e8f..583a29faa7 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -7,9 +7,11 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; -import { BackstageTheme } from '@backstage/theme'; -import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; -import { CardHeaderProps } from '@material-ui/core/CardHeader'; +import { BottomNavigationActionProps } from '@material-ui/core'; +import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; +import { ButtonProps } from '@material-ui/core'; +import { ButtonTypeMap } from '@material-ui/core'; +import { CardHeaderProps } from '@material-ui/core'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; @@ -632,6 +634,13 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element; // @public (undocumented) export type MissingAnnotationEmptyStateClassKey = 'code'; +// Warning: (ae-missing-release-tag) "MobileSidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const MobileSidebar: ({ + children, +}: React_2.PropsWithChildren<{}>) => JSX.Element | null; + // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -771,7 +780,10 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Sidebar(props: PropsWithChildren): JSX.Element; +export const Sidebar: ({ + children, + ...props +}: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -800,6 +812,7 @@ export const sidebarConfig: { selectedIndicatorWidth: number; userBadgePadding: number; userBadgeDiameter: number; + mobileSidebarHeight: number; }; // Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1083,6 +1096,18 @@ export const SidebarDivider: React_2.ComponentType< } >; +// Warning: (ae-forgotten-export) The symbol "SidebarGroupProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public +export const SidebarGroup: ({ + to, + label, + icon, + value, + children, +}: React_2.PropsWithChildren) => JSX.Element; + // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index ead77d4cae..5bcd8ec789 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -154,13 +154,16 @@ const DesktopSidebar = ({ ); }; -export const Sidebar = ({ children }: React.PropsWithChildren<{}>) => { +export const Sidebar = ({ + children, + ...props +}: React.PropsWithChildren) => { const isMobileScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); return isMobileScreen ? ( - {children} + {children} ) : ( {children} ); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 198e7ec2b1..d07f8abae7 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -14,28 +14,27 @@ * limitations under the License. */ -import React, { useContext, PropsWithChildren } from 'react'; -import { Link, makeStyles } from '@material-ui/core'; -import HomeIcon from '@material-ui/icons/Home'; -import ExtensionIcon from '@material-ui/icons/Extension'; -import MapIcon from '@material-ui/icons/MyLocation'; -import LibraryBooks from '@material-ui/icons/LibraryBooks'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import LogoFull from './LogoFull'; -import LogoIcon from './LogoIcon'; -import { NavLink } from 'react-router-dom'; -import { Settings as SidebarSettings } from '@backstage/plugin-user-settings'; +import { + Sidebar, sidebarConfig, + SidebarContext, SidebarDivider, SidebarGroup, SidebarItem, SidebarPage, SidebarScrollWrapper, SidebarSpace +} from '@backstage/core-components'; import { SidebarSearch } from '@backstage/plugin-search'; import { - Sidebar, - SidebarPage, - sidebarConfig, - SidebarContext, - SidebarItem, - SidebarDivider, - SidebarSpace, - SidebarScrollWrapper, -} from '@backstage/core-components'; + Settings as SidebarSettings, + UserSettingsSignInAvatar +} from '@backstage/plugin-user-settings'; +import { Link, makeStyles } from '@material-ui/core'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import ExtensionIcon from '@material-ui/icons/Extension'; +import HomeIcon from '@material-ui/icons/Home'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import MenuIcon from '@material-ui/icons/Menu'; +import MapIcon from '@material-ui/icons/MyLocation'; +import SearchIcon from '@material-ui/icons/Search'; +import React, { PropsWithChildren, useContext } from 'react'; +import { NavLink } from 'react-router-dom'; +import LogoFull from './LogoFull'; +import LogoIcon from './LogoIcon'; const useSidebarLogoStyles = makeStyles({ root: { @@ -74,8 +73,11 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - + } to="/search"> + + + }> {/* Global nav, not org-specific */} @@ -86,9 +88,16 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + - + } + to="/settings" + > + + {children} From 4313e9167af56f875e7791c83bd0ed3fc569b541 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 23 Aug 2021 10:00:17 +0200 Subject: [PATCH 008/116] Prettier fix create-app templates Signed-off-by: Philipp Hugenroth --- .../default-app/packages/app/src/components/Root/Root.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index d07f8abae7..22d8fdd705 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -57,6 +57,7 @@ const SidebarLogo = () => { return (
+ Date: Mon, 23 Aug 2021 10:14:06 +0200 Subject: [PATCH 009/116] Prettier Signed-off-by: Philipp Hugenroth --- .../packages/app/src/components/Root/Root.tsx | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 22d8fdd705..4330ab9bcd 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -15,13 +15,20 @@ */ import { - Sidebar, sidebarConfig, - SidebarContext, SidebarDivider, SidebarGroup, SidebarItem, SidebarPage, SidebarScrollWrapper, SidebarSpace + Sidebar, + sidebarConfig, + SidebarContext, + SidebarDivider, + SidebarGroup, + SidebarItem, + SidebarPage, + SidebarScrollWrapper, + SidebarSpace, } from '@backstage/core-components'; import { SidebarSearch } from '@backstage/plugin-search'; import { Settings as SidebarSettings, - UserSettingsSignInAvatar + UserSettingsSignInAvatar, } from '@backstage/plugin-user-settings'; import { Link, makeStyles } from '@material-ui/core'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; @@ -57,7 +64,6 @@ const SidebarLogo = () => { return (
- ) => ( }> - {/* Global nav, not org-specific */} - - - - - {/* End global nav */} - - - - + {/* Global nav, not org-specific */} + + + + + {/* End global nav */} + + + + From aa0d73424a86408a5aa236a4fe3cab49b5360c1e Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 23 Aug 2021 13:18:52 +0200 Subject: [PATCH 010/116] Add priority prop to add possibility for rearranging SidebarGroups for the mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/app/src/components/Root/Root.tsx | 2 +- .../src/layout/Sidebar/MobileSidebar.tsx | 43 +++++++++++++++---- .../src/layout/Sidebar/SidebarGroup.tsx | 6 +-- .../packages/app/src/components/Root/Root.tsx | 2 +- 4 files changed, 37 insertions(+), 16 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 1851145a31..d4a791173b 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -89,7 +89,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 633f366501..93fc4d58c5 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -69,6 +69,22 @@ const useStyles = makeStyles(theme => ({ }, })); +const sortSidebarGroupsForPriority = ( + childA: React.ReactElement, + childB: React.ReactElement, +) => { + const priorityADefined = childA.props.priority !== undefined; + const priorityBDefined = childB.props.priority !== undefined; + if (priorityADefined && !priorityBDefined) { + return -1; + } else if (priorityBDefined && !priorityADefined) { + return 1; + } else if (priorityADefined && priorityBDefined) { + return childA.props.priority - childB.props.priority; + } + return 0; +}; + const OverlayMenu = ({ children, label, @@ -97,33 +113,42 @@ export const MobileSidebarContext = createContext({ setSelectedMenuItemIndex: () => {}, }); -/** - * Filters for sidebar groups and reorders them to create a custom BottomNavigation - */ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { const classes = useStyles(); const location = useLocation(); const [selectedMenuItemIndex, setSelectedMenuItemIndex] = useState(-1); + let shouldSortSidebarGroups = false; useEffect(() => { - // This is getting triggered to often - fix me! setSelectedMenuItemIndex(-1); }, [location.pathname]); - const sidebarGroups = React.Children.map(children, child => - React.isValidElement(child) && child.type === SidebarGroup ? child : null, - ); + // Filter children for SidebarGroups & set `shouldSortSidebarGroups` if priorities are set for one or more SidebarGroups + let sidebarGroups = React.Children.map(children, child => { + if (React.isValidElement(child) && child.type === SidebarGroup) { + if (child.props.priority !== undefined) { + shouldSortSidebarGroups = true; + } + return child; + } + return null; + }); if (!sidebarGroups) { - return null; // think about the exception state + // If Sidebar has no children the MobileSidebar won't be rendered + return null; } else if (!sidebarGroups.length) { - // Render default SidebarGroup if no + // If Sidebar has no SidebarGroup as a children a default + // SidebarGroup with the complete Sidebar content will be created sidebarGroups.push( }> {children} , ); + } else if (shouldSortSidebarGroups) { + // If a SidebarGroup has a given priority the SidebarGroups are sorted for prioirty + sidebarGroups = sidebarGroups.sort(sortSidebarGroupsForPriority); } const shouldShowGroupChildren = diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 75dbb9ae91..c7dfe0fc21 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -30,6 +30,7 @@ import { MobileSidebarContext } from './MobileSidebar'; interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; + priority?: number; } const useStyles = makeStyles(theme => ({ @@ -50,11 +51,6 @@ const useStyles = makeStyles(theme => ({ }, })); -/** - * If the page is mobile it should be BottomNavigationAction - otherwise just a fragment - * Links to page, which will be displayed - * - If 'to' Prop is not defined it will render a Menu page out of the children (if children given) - */ export const SidebarGroup = ({ to, label, diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 4330ab9bcd..434400511a 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -84,7 +84,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} From 29715dd22bd7778f63b45074cbd00973493d799f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 23 Aug 2021 14:04:10 +0200 Subject: [PATCH 011/116] Generate api-report Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 583a29faa7..2ab91c9711 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -636,7 +636,7 @@ export type MissingAnnotationEmptyStateClassKey = 'code'; // Warning: (ae-missing-release-tag) "MobileSidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public +// @public (undocumented) export const MobileSidebar: ({ children, }: React_2.PropsWithChildren<{}>) => JSX.Element | null; @@ -1099,7 +1099,7 @@ export const SidebarDivider: React_2.ComponentType< // Warning: (ae-forgotten-export) The symbol "SidebarGroupProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public +// @public (undocumented) export const SidebarGroup: ({ to, label, From 8a08a9e7af3b5b908a0bb9d4fc22b0cd6c855187 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 25 Aug 2021 00:26:48 +0200 Subject: [PATCH 012/116] Add tests for MobileSidebar & SidebarGroup Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/MobileSidebar.test.tsx | 96 +++++++++++++++++++ .../src/layout/Sidebar/MobileSidebar.tsx | 6 +- .../src/layout/Sidebar/SidebarGroup.test.tsx | 66 +++++++++++++ 3 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx create mode 100644 packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx new file mode 100644 index 0000000000..6f6463a3dc --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx @@ -0,0 +1,96 @@ +/* + * 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 { mockBreakpoint, renderInTestApp } from '@backstage/test-utils'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import HomeIcon from '@material-ui/icons/Home'; +import LayersIcon from '@material-ui/icons/Layers'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import { fireEvent } from '@testing-library/react'; +import React from 'react'; +import { Sidebar } from './Bar'; +import { SidebarItem } from './Items'; +import { MobileSidebar } from './MobileSidebar'; +import { SidebarGroup } from './SidebarGroup'; + +const MobileSidebarWithGroups = () => ( + +

Header

+ } label="Menu"> + + + + +
Content
+
More Content
+ } label="Create" to="#" /> +
Footer
+
+); + +const MobileSidebarWithoutGroups = () => ( + + + + + +); + +describe('', () => { + beforeEach(() => { + mockBreakpoint({ matches: true }); + }); + + it('should render MobileSidebar on smaller screens', async () => { + const { getByTestId } = await renderInTestApp( + + + , + ); + expect(getByTestId('mobile-sidebar-root')).toBeVisible(); + }); + + it('should render only SidebarGroups inside MobileSidebar', async () => { + const { findAllByRole, getByTestId, findByText } = await renderInTestApp( + , + ); + expect(getByTestId('mobile-sidebar-root').children.length).toBe(2); + expect((await findAllByRole('button')).length).toBe(2); + expect(await findByText('Menu')).toBeValid(); + expect(await findByText('Create')).toBeValid(); + }); + + it('should render default MobileSidebar when there are no SidebarGroups', async () => { + const { findAllByRole, getByTestId } = await renderInTestApp( + , + ); + expect(getByTestId('mobile-sidebar-root').children.length).toBe(1); + const defaultSidebarGroup = await findAllByRole('button'); + expect(defaultSidebarGroup.length).toBe(1); + }); + + it('should render OverlayMenu displaying SidebarItems', async () => { + const { findByText, getByRole } = await renderInTestApp( + , + ); + const menuButton = await findByText('Menu'); + fireEvent.click(menuButton); + expect(getByRole('heading', { name: 'Menu' })).toBeVisible(); + expect(getByRole('link', { name: 'Home' })).toBeVisible(); + expect(getByRole('link', { name: 'Explore' })).toBeVisible(); + expect(getByRole('link', { name: 'Docs' })).toBeVisible(); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 93fc4d58c5..881a933efd 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -43,6 +43,7 @@ const useStyles = makeStyles(theme => ({ left: 0, right: 0, zIndex: 1000, + // SidebarDivider color borderTop: '1px solid #383838', }, @@ -165,7 +166,10 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { onClose={() => setSelectedMenuItemIndex(-1)} /> )} - + {sidebarGroups} diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx new file mode 100644 index 0000000000..664495067c --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -0,0 +1,66 @@ +/* + * 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 { mockBreakpoint, renderInTestApp } from '@backstage/test-utils'; +import HomeIcon from '@material-ui/icons/Home'; +import LayersIcon from '@material-ui/icons/Layers'; +import LibraryBooks from '@material-ui/icons/LibraryBooks'; +import { fireEvent } from '@testing-library/react'; +import React from 'react'; +import { SidebarItem } from './Items'; +import { MobileSidebarContext } from './MobileSidebar'; +import { SidebarGroup } from './SidebarGroup'; + +const SidebarGroupWithItems = () => ( + } label="Menu"> + + + + +); + +describe('', () => { + it('should render Items in BottomNavigationAciton on small screens', async () => { + mockBreakpoint({ matches: true }); + const { findByRole, container } = await renderInTestApp( + , + ); + expect(container.childNodes.length).toBe(1); + expect(await findByRole('button')).toBeVisible(); + }); + + it('should render Items without wrapper on bigger screens', async () => { + mockBreakpoint({ matches: false }); + const { container } = await renderInTestApp(); + expect(container.childNodes.length).toBe(3); + }); + + it('should trigger update of MobileSidebarContext', async () => { + mockBreakpoint({ matches: true }); + const value = { + selectedMenuItemIndex: -1, + setSelectedMenuItemIndex: jest.fn(), + }; + const { findByRole } = await renderInTestApp( + + + , + ); + const group = await findByRole('button'); + fireEvent.click(group); + expect(value.setSelectedMenuItemIndex).toHaveBeenCalled(); + }); +}); From 19c9fdc2584a9e24d9501fe55a99a91c740676cc Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 25 Aug 2021 10:07:49 +0200 Subject: [PATCH 013/116] Cleanup before review Signed-off-by: Philipp Hugenroth --- .../core-components/src/layout/Sidebar/Sidebar.stories.tsx | 7 +------ packages/core-components/src/layout/Sidebar/config.ts | 2 +- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 5aa279d846..5ef4bd8915 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -39,15 +39,10 @@ export default { ], }; -const handleSearch = (input: string) => { - // eslint-disable-next-line no-console - console.log(input); -}; - export const SampleSidebar = () => ( - + {}} to="/search" /> diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index e213385f73..a90fac9672 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -46,5 +46,5 @@ export type SidebarContextType = { }; export const SidebarContext = createContext({ - isOpen: true, + isOpen: false, }); From 5707087ba7d2f6985577cdbae870a7ad35a09315 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 25 Aug 2021 14:23:59 +0200 Subject: [PATCH 014/116] Improve Sidebar Storybook Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/Sidebar.stories.tsx | 61 ++++++++++++++----- 1 file changed, 45 insertions(+), 16 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 5ef4bd8915..d99b96aebf 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -14,11 +14,18 @@ * limitations under the License. */ +// We don't want to export RoutingProvider from core-app-api, but it's way easier to +// use here. This hack only works in storybook stories. +// TODO: Export a nicer to user routing provider, perhaps from test-utils +// eslint-disable-next-line monorepo/no-internal-import +import { RoutingProvider } from '@backstage/core-app-api/src/routing/RoutingProvider'; +import { createRouteRef } from '@backstage/core-plugin-api'; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; +import ExtensionIcon from '@material-ui/icons/Extension'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import MenuIcon from '@material-ui/icons/Menu'; -import React from 'react'; -import { MemoryRouter } from 'react-router-dom'; +import React, { ComponentType } from 'react'; +import { MemoryRouter, useLocation } from 'react-router-dom'; import { Sidebar, SidebarDivider, @@ -28,28 +35,50 @@ import { SidebarSearchField, SidebarSpace, } from '.'; +import { SidebarPage } from './Page'; + +const routeRef = createRouteRef({ + id: 'storybook.test-route', +}); + +const Location = () => { + const location = useLocation(); + return
Current location: {location.pathname}
; +}; export default { title: 'Layout/Sidebar', component: Sidebar, decorators: [ - (storyFn: () => JSX.Element) => ( - {storyFn()} + (Story: ComponentType<{}>) => ( + + + + + ), ], }; export const SampleSidebar = () => ( - - - {}} to="/search" /> - - - - - - - - - + + + + {}} /> + + + + + + + + + + + ); From c260b564351848d6b792460172a165b1fe468513 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 26 Aug 2021 16:06:35 +0200 Subject: [PATCH 015/116] Fix hidden text for mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/core-components/src/layout/Sidebar/Bar.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 5bcd8ec789..1770289c08 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -163,7 +163,13 @@ export const Sidebar = ({ ); return isMobileScreen ? ( - {children} + + {children} + ) : ( {children} ); From 5885ac0f7d0dd9fc47d0efb7fec76e093c0ca032 Mon Sep 17 00:00:00 2001 From: tudi2d Date: Mon, 6 Sep 2021 12:34:00 +0200 Subject: [PATCH 016/116] Fix SearchPage imports Signed-off-by: tudi2d --- packages/app/src/components/search/SearchPage.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index fefed84f58..bb97993676 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -25,8 +25,6 @@ import { SearchType, } from '@backstage/plugin-search'; import { DocsResultListItem } from '@backstage/plugin-techdocs'; -import { SearchResultSet } from '@backstage/search-common'; -import { BackstageTheme } from '@backstage/theme'; import { Grid, List, @@ -35,8 +33,8 @@ import { Theme, useMediaQuery, } from '@material-ui/core'; -import Pagination from '@material-ui/lab/Pagination'; -import React, { useState } from 'react'; +import React from 'react'; +import { BackstageTheme } from '../../../../theme/src'; const useStyles = makeStyles((theme: Theme) => ({ bar: { From 84865c1e1c474cb43650d0dc21329f190c569c92 Mon Sep 17 00:00:00 2001 From: tudi2d Date: Thu, 23 Sep 2021 23:45:41 +0200 Subject: [PATCH 017/116] Fix linting error Signed-off-by: tudi2d --- packages/app/src/components/search/SearchPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index bb97993676..2a21ad0e86 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -34,7 +34,7 @@ import { useMediaQuery, } from '@material-ui/core'; import React from 'react'; -import { BackstageTheme } from '../../../../theme/src'; +import { BackstageTheme } from '@backstage/theme'; const useStyles = makeStyles((theme: Theme) => ({ bar: { From e6fd475243037ab474630e41f8808a3894c926b1 Mon Sep 17 00:00:00 2001 From: tudi2d Date: Wed, 29 Sep 2021 13:49:36 +0200 Subject: [PATCH 018/116] Fix api-report... Signed-off-by: tudi2d --- packages/core-components/api-report.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 2ab91c9711..978b65d43e 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -7,6 +7,7 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; +import { BackstageTheme } from '@backstage/theme'; import { BottomNavigationActionProps } from '@material-ui/core'; import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; import { ButtonProps } from '@material-ui/core'; @@ -780,10 +781,10 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const Sidebar: ({ +export function Sidebar({ children, ...props -}: React_2.PropsWithChildren) => JSX.Element; +}: React_2.PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // From 80cfeaec664482aaa05c076e8832dde0f4f1f05b Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 9 Nov 2021 15:45:38 +0100 Subject: [PATCH 019/116] Update imports & api-reports Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 10 ++++------ .../src/layout/Sidebar/MobileSidebar.tsx | 12 +++++------- .../src/layout/Sidebar/SidebarGroup.tsx | 9 ++++----- 3 files changed, 13 insertions(+), 18 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 978b65d43e..b39a0509f5 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -8,11 +8,9 @@ import { ApiRef } from '@backstage/core-plugin-api'; import { BackstageIdentityApi } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; -import { BottomNavigationActionProps } from '@material-ui/core'; -import { Breadcrumbs as Breadcrumbs_2 } from '@material-ui/core'; -import { ButtonProps } from '@material-ui/core'; -import { ButtonTypeMap } from '@material-ui/core'; -import { CardHeaderProps } from '@material-ui/core'; +import { BottomNavigationActionProps } from '@material-ui/core/BottomNavigationAction'; +import { ButtonProps as ButtonProps_2 } from '@material-ui/core/Button'; +import { CardHeaderProps } from '@material-ui/core/CardHeader'; import { Column } from '@material-table/core'; import { ComponentClass } from 'react'; import { ComponentProps } from 'react'; @@ -784,7 +782,7 @@ export type SelectInputBaseClassKey = 'root' | 'input'; export function Sidebar({ children, ...props -}: React_2.PropsWithChildren): JSX.Element; +}: React_2.PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 881a933efd..25202c12b0 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -15,13 +15,11 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { - BottomNavigation, - Box, - IconButton, - makeStyles, - Typography, -} from '@material-ui/core'; +import BottomNavigation from '@material-ui/core/BottomNavigation'; +import Box from '@material-ui/core/Box'; +import IconButton from '@material-ui/core/IconButton'; +import { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; import CloseIcon from '@material-ui/icons/Close'; import MenuIcon from '@material-ui/icons/Menu'; import React, { createContext, useEffect, useState } from 'react'; diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index c7dfe0fc21..2c971c7941 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -16,12 +16,11 @@ */ import { BackstageTheme } from '@backstage/theme'; -import { - BottomNavigationAction, +import BottomNavigationAction, { BottomNavigationActionProps, - makeStyles, - useMediaQuery, -} from '@material-ui/core'; +} from '@material-ui/core/BottomNavigationAction'; +import { makeStyles } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; import { Link } from '../../components'; From 14856682398b4214864b30aec366c6a4c2a1b260 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 10 Nov 2021 16:04:32 +0100 Subject: [PATCH 020/116] Cleanup after rebase & adjust components to use class keys Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 9 +- .../core-components/src/layout/Page/Page.tsx | 4 +- .../src/layout/Sidebar/Items.tsx | 179 ++++++++++-------- .../src/layout/Sidebar/Page.tsx | 4 +- .../src/overridableComponents.ts | 8 - 5 files changed, 105 insertions(+), 99 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index b39a0509f5..16cdda0f81 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -779,10 +779,10 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function Sidebar({ +export const Sidebar: ({ children, ...props -}: React_2.PropsWithChildren): JSX.Element; +}: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -790,11 +790,6 @@ export function Sidebar({ export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; -// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; - // Warning: (ae-missing-release-tag) "sidebarConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 9f49174977..1810ef8177 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -16,9 +16,11 @@ import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; -import { makeStyles, ThemeProvider } from '@material-ui/core'; +import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; import { sidebarConfig } from '../Sidebar'; +export type PageClassKey = 'root'; + const useStyles = makeStyles(theme => ({ root: { display: 'grid', diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 19fd8c0c0a..121f164fe2 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -38,83 +38,36 @@ import { } from 'react-router-dom'; import { sidebarConfig, SidebarContext } from './config'; +export type SidebarItemClassKey = + | 'root' + | 'buttonItem' + | 'closed' + | 'open' + | 'label' + | 'iconContainer' + | 'searchRoot' + | 'searchField' + | 'searchFieldHTMLInput' + | 'searchContainer' + | 'secondaryAction' + | 'selected'; -const useStyles = makeStyles(theme => { - const { - selectedIndicatorWidth, - drawerWidthClosed, - drawerWidthOpen, - iconContainerWidth, - } = sidebarConfig; - return { - root: { - color: theme.palette.navigation.color, - display: 'flex', - flexFlow: 'row nowrap', - alignItems: 'center', - height: 48, - cursor: 'pointer', - }, - buttonItem: { - background: 'none', - border: 'none', - width: 'auto', - margin: 0, - padding: 0, - textAlign: 'inherit', - font: 'inherit', - }, - closed: { - width: drawerWidthClosed, - justifyContent: 'center', - }, - open: { - [theme.breakpoints.up('sm')]: { - width: drawerWidthOpen, - }, - }, - label: { - // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs - fontWeight: 'bold', - whiteSpace: 'nowrap', - lineHeight: 'auto', - flex: '3 1 auto', - width: '110px', - overflow: 'hidden', - 'text-overflow': 'ellipsis', - }, - iconContainer: { - boxSizing: 'border-box', - height: '100%', - width: iconContainerWidth, - marginRight: -theme.spacing(2), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - searchRoot: { - marginBottom: 12, - }, - searchField: { - color: '#b5b5b5', - fontWeight: 'bold', - fontSize: theme.typography.fontSize, - }, - searchFieldHTMLInput: { - padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, - }, - searchContainer: { - width: drawerWidthOpen - iconContainerWidth, - }, - secondaryAction: { - width: theme.spacing(6), - textAlign: 'center', - marginRight: theme.spacing(1), - }, - selected: { - '&$root': { - borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, - color: theme.palette.navigation.selectedColor, +const useStyles = makeStyles( + theme => { + const { + selectedIndicatorWidth, + drawerWidthClosed, + drawerWidthOpen, + iconContainerWidth, + } = sidebarConfig; + return { + root: { + color: theme.palette.navigation.color, + display: 'flex', + flexFlow: 'row nowrap', + alignItems: 'center', + height: 48, + cursor: 'pointer', }, buttonItem: { background: 'none', @@ -130,7 +83,9 @@ const useStyles = makeStyles(theme => { justifyContent: 'center', }, open: { - width: drawerWidthOpen, + [theme.breakpoints.up('sm')]: { + width: drawerWidthOpen, + }, }, label: { // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs @@ -175,15 +130,75 @@ const useStyles = makeStyles(theme => { borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, color: theme.palette.navigation.selectedColor, }, - '&$closed': { - width: drawerWidthClosed - selectedIndicatorWidth, + buttonItem: { + background: 'none', + border: 'none', + width: 'auto', + margin: 0, + padding: 0, + textAlign: 'inherit', + font: 'inherit', }, - '& $iconContainer': { - marginLeft: -selectedIndicatorWidth, + closed: { + width: drawerWidthClosed, + justifyContent: 'center', + }, + open: { + width: drawerWidthOpen, + }, + label: { + // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs + fontWeight: 'bold', + whiteSpace: 'nowrap', + lineHeight: 'auto', + flex: '3 1 auto', + width: '110px', + overflow: 'hidden', + 'text-overflow': 'ellipsis', + }, + iconContainer: { + boxSizing: 'border-box', + height: '100%', + width: iconContainerWidth, + marginRight: -theme.spacing(2), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + }, + searchRoot: { + marginBottom: 12, + }, + searchField: { + color: '#b5b5b5', + fontWeight: 'bold', + fontSize: theme.typography.fontSize, + }, + searchFieldHTMLInput: { + padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + }, + searchContainer: { + width: drawerWidthOpen - iconContainerWidth, + }, + secondaryAction: { + width: theme.spacing(6), + textAlign: 'center', + marginRight: theme.spacing(1), + }, + selected: { + '&$root': { + borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, + color: theme.palette.navigation.selectedColor, + }, + '&$closed': { + width: drawerWidthClosed - selectedIndicatorWidth, + }, + '& $iconContainer': { + marginLeft: -selectedIndicatorWidth, + }, }, }, }; - }}, + }, { name: 'BackstageSidebarItem' }, ); diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index cecc985a28..7c7a38d363 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core'; +import { makeStyles } from '@material-ui/core/styles'; import React, { createContext, PropsWithChildren, @@ -25,6 +25,8 @@ import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; +export type SidebarPageClassKey = 'root'; + const useStyles = makeStyles( theme => ({ root: { diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index af35445e24..3dc208eb70 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -80,11 +80,7 @@ import { CardActionsTopRightClassKey, ItemCardGridClassKey, ItemCardHeaderClassKey, - PageClassKey, - SidebarClassKey, SidebarIntroClassKey, - SidebarItemClassKey, - SidebarPageClassKey, CustomProviderClassKey, SignInPageClassKey, TabbedCardClassKey, @@ -153,11 +149,7 @@ type BackstageComponentsNameToClassKey = { BackstageInfoCardCardActionsTopRight: CardActionsTopRightClassKey; BackstageItemCardGrid: ItemCardGridClassKey; BackstageItemCardHeader: ItemCardHeaderClassKey; - BackstagePage: PageClassKey; - BackstageSidebar: SidebarClassKey; BackstageSidebarIntro: SidebarIntroClassKey; - BackstageSidebarItem: SidebarItemClassKey; - BackstageSidebarPage: SidebarPageClassKey; BackstageCustomProvider: CustomProviderClassKey; BackstageSignInPage: SignInPageClassKey; BackstageTabbedCard: TabbedCardClassKey; From 39f1c063258c9e1ba124711a2f99cbf94211b15c Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 14:35:39 +0100 Subject: [PATCH 021/116] Adjust mobile-sidebar to updated code base Remove to deep changes Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 16 +++-- .../src/layout/Header/Header.tsx | 2 +- .../core-components/src/layout/Page/Page.tsx | 12 +--- .../src/layout/Sidebar/MobileSidebar.tsx | 56 +++++++----------- .../src/layout/Sidebar/Sidebar.stories.tsx | 59 ++++++++----------- .../src/layout/Sidebar/SidebarGroup.tsx | 28 ++++----- .../src/layout/Sidebar/index.ts | 1 + 7 files changed, 75 insertions(+), 99 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 16cdda0f81..701a608ece 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -1090,18 +1090,24 @@ export const SidebarDivider: React_2.ComponentType< } >; -// Warning: (ae-forgotten-export) The symbol "SidebarGroupProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export const SidebarGroup: ({ - to, - label, - icon, - value, children, + ...props }: React_2.PropsWithChildren) => JSX.Element; +// Warning: (ae-missing-release-tag) "SidebarGroupProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export interface SidebarGroupProps extends BottomNavigationActionProps { + // (undocumented) + priority?: number; + // (undocumented) + to?: string; +} + // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 40495ca0e0..92f28c1d07 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -64,7 +64,7 @@ const useStyles = makeStyles( }, title: { color: theme.palette.bursts.fontColor, - wordBreak: 'break-all', + // ? fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', marginBottom: 0, }, diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index 1810ef8177..ba3c8bfff7 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -14,26 +14,20 @@ * limitations under the License. */ -import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; -import { sidebarConfig } from '../Sidebar'; +import React, { PropsWithChildren } from 'react'; export type PageClassKey = 'root'; -const useStyles = makeStyles(theme => ({ +const useStyles = makeStyles(() => ({ root: { display: 'grid', gridTemplateAreas: "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", gridTemplateRows: 'max-content auto 1fr', gridTemplateColumns: 'auto 1fr auto', - [theme.breakpoints.up('sm')]: { - height: '100vh', - }, - [theme.breakpoints.down('xs')]: { - height: `calc(100vh - ${sidebarConfig.mobileSidebarHeight}px)`, - }, + height: '100vh', overflowY: 'auto', }, })); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 25202c12b0..247c191dee 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -14,6 +14,7 @@ * limitations under the License. */ +import { useElementFilter } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import BottomNavigation from '@material-ui/core/BottomNavigation'; import Box from '@material-ui/core/Box'; @@ -22,6 +23,7 @@ import { makeStyles } from '@material-ui/core/styles'; import Typography from '@material-ui/core/Typography'; import CloseIcon from '@material-ui/icons/Close'; import MenuIcon from '@material-ui/icons/Menu'; +import { orderBy } from 'lodash'; import React, { createContext, useEffect, useState } from 'react'; import { useLocation } from 'react-router'; import { sidebarConfig } from './config'; @@ -68,27 +70,18 @@ const useStyles = makeStyles(theme => ({ }, })); -const sortSidebarGroupsForPriority = ( - childA: React.ReactElement, - childB: React.ReactElement, -) => { - const priorityADefined = childA.props.priority !== undefined; - const priorityBDefined = childB.props.priority !== undefined; - if (priorityADefined && !priorityBDefined) { - return -1; - } else if (priorityBDefined && !priorityADefined) { - return 1; - } else if (priorityADefined && priorityBDefined) { - return childA.props.priority - childB.props.priority; - } - return 0; -}; +const sortSidebarGroupsForPriority = (children: React.ReactElement[]) => + orderBy( + children, + ({ props: { priority } }) => (priority ? priority : -1), + 'desc', + ); const OverlayMenu = ({ children, - label, + label = 'Menu', onClose, -}: React.PropsWithChildren<{ label: string; onClose: () => void }>) => { +}: React.PropsWithChildren<{ label?: string; onClose: () => void }>) => { const classes = useStyles(); return ( @@ -117,22 +110,19 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { const location = useLocation(); const [selectedMenuItemIndex, setSelectedMenuItemIndex] = useState(-1); - let shouldSortSidebarGroups = false; useEffect(() => { setSelectedMenuItemIndex(-1); }, [location.pathname]); - // Filter children for SidebarGroups & set `shouldSortSidebarGroups` if priorities are set for one or more SidebarGroups - let sidebarGroups = React.Children.map(children, child => { - if (React.isValidElement(child) && child.type === SidebarGroup) { - if (child.props.priority !== undefined) { - shouldSortSidebarGroups = true; - } - return child; - } - return null; - }); + // Filter children for SidebarGroups + let sidebarGroups = useElementFilter(children, elements => + elements + .getElements() + .filter( + child => React.isValidElement(child) && child.type === SidebarGroup, + ), + ); if (!sidebarGroups) { // If Sidebar has no children the MobileSidebar won't be rendered @@ -141,13 +131,11 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { // If Sidebar has no SidebarGroup as a children a default // SidebarGroup with the complete Sidebar content will be created sidebarGroups.push( - }> - {children} - , + }>{children}, ); - } else if (shouldSortSidebarGroups) { - // If a SidebarGroup has a given priority the SidebarGroups are sorted for prioirty - sidebarGroups = sidebarGroups.sort(sortSidebarGroupsForPriority); + } else { + // Sort SidebarGroups for the given Priority + sidebarGroups = sortSidebarGroupsForPriority(sidebarGroups); } const shouldShowGroupChildren = diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index d99b96aebf..e487f9758b 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -13,19 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -// We don't want to export RoutingProvider from core-app-api, but it's way easier to -// use here. This hack only works in storybook stories. -// TODO: Export a nicer to user routing provider, perhaps from test-utils -// eslint-disable-next-line monorepo/no-internal-import -import { RoutingProvider } from '@backstage/core-app-api/src/routing/RoutingProvider'; import { createRouteRef } from '@backstage/core-plugin-api'; +import { wrapInTestApp } from '@backstage/test-utils'; import AddCircleOutlineIcon from '@material-ui/icons/AddCircleOutline'; import ExtensionIcon from '@material-ui/icons/Extension'; import HomeOutlinedIcon from '@material-ui/icons/HomeOutlined'; import MenuIcon from '@material-ui/icons/Menu'; import React, { ComponentType } from 'react'; -import { MemoryRouter, useLocation } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { Sidebar, SidebarDivider, @@ -50,35 +45,27 @@ export default { title: 'Layout/Sidebar', component: Sidebar, decorators: [ - (Story: ComponentType<{}>) => ( - - - - - - ), + (Story: ComponentType<{}>) => + wrapInTestApp(, { mountedRoutes: { '/': routeRef } }), ], }; -export const SampleSidebar = () => ( - - - - {}} /> - - - - - - - - - - - -); +export const SampleSidebar = () => { + return ( + + + + {}} /> + + + + + + + + + + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 2c971c7941..d71657ac5a 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -27,7 +27,7 @@ import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; -interface SidebarGroupProps extends BottomNavigationActionProps { +export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; priority?: number; } @@ -50,20 +50,11 @@ const useStyles = makeStyles(theme => ({ }, })); -export const SidebarGroup = ({ - to, - label, - icon, - value, - children, -}: React.PropsWithChildren) => { +const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { const classes = useStyles(); const location = useLocation(); const { selectedMenuItemIndex, setSelectedMenuItemIndex } = useContext(MobileSidebarContext); - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); const onChange = (_: React.ChangeEvent<{}>, value: number) => { if (value === selectedMenuItemIndex) { @@ -79,7 +70,7 @@ export const SidebarGroup = ({ !(selectedMenuItemIndex >= 0) && to === location.pathname); - return isMobileScreen ? ( + return ( // Material UI issue: https://github.com/mui-org/material-ui/issues/27820 // @ts-ignore - ) : ( - <>{children} ); }; + +export const SidebarGroup = ({ + children, + ...props +}: React.PropsWithChildren) => { + const isMobileScreen = useMediaQuery(theme => + theme.breakpoints.down('xs'), + ); + + return isMobileScreen ? : <>{children}; +}; diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 26a4c0e8a5..14d8d35b48 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -17,6 +17,7 @@ export { Sidebar } from './Bar'; export { MobileSidebar } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; +export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; export { From aa2e6117070ec5ba4539849709a5ddd308d1ab42 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 16:03:50 +0100 Subject: [PATCH 022/116] Update api report after merge Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index d3c41a3652..a03e8c1447 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -802,7 +802,7 @@ export type SelectInputBaseClassKey = 'root' | 'input'; export const Sidebar: ({ children, ...props -}: React_2.PropsWithChildren) => JSX.Element; +}: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // From e3ca78b5a601dc51f2b2cc086e284d34d1fba9c3 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 16:48:03 +0100 Subject: [PATCH 023/116] Run prettier on default-app Signed-off-by: Philipp Hugenroth --- .../packages/app/src/components/Root/Root.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 54ca803773..23670dcb63 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -23,10 +23,13 @@ import { SidebarItem, SidebarPage, SidebarScrollWrapper, - SidebarSpace + SidebarSpace, } from '@backstage/core-components'; import { SidebarSearchModal } from '@backstage/plugin-search'; -import { Settings as SidebarSettings, UserSettingsSignInAvatar } from '@backstage/plugin-user-settings'; +import { + Settings as SidebarSettings, + UserSettingsSignInAvatar, +} from '@backstage/plugin-user-settings'; import { Link, makeStyles } from '@material-ui/core'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; import ExtensionIcon from '@material-ui/icons/Extension'; @@ -78,7 +81,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( } to="/search"> - + }> From 882adc942efb62dcd6298704c10027809c3e6843 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 18:33:08 +0100 Subject: [PATCH 024/116] Add padding to SidebarPage to avoid MobileSidebar overlapping content Signed-off-by: Philipp Hugenroth --- packages/core-components/src/layout/Page/Page.tsx | 2 +- packages/core-components/src/layout/Sidebar/Page.tsx | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index ba3c8bfff7..a0c84bb56a 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -27,7 +27,7 @@ const useStyles = makeStyles(() => ({ "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", gridTemplateRows: 'max-content auto 1fr', gridTemplateColumns: 'auto 1fr auto', - height: '100vh', + height: '100%', overflowY: 'auto', }, })); diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 7c7a38d363..20faec2f30 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -31,7 +31,6 @@ const useStyles = makeStyles( theme => ({ root: { width: '100%', - minHeight: '100%', transition: 'padding-left 0.1s ease-out', [theme.breakpoints.up('sm')]: { paddingLeft: ({ isPinned }) => @@ -39,6 +38,9 @@ const useStyles = makeStyles( ? sidebarConfig.drawerWidthOpen : sidebarConfig.drawerWidthClosed, }, + [theme.breakpoints.down('xs')]: { + paddingBottom: sidebarConfig.mobileSidebarHeight, + }, }, }), { name: 'BackstageSidebarPage' }, From 4ead01ef5ab6a05015dd329ae58172ebd8647159 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 11 Nov 2021 19:02:09 +0100 Subject: [PATCH 025/116] Fix overlapping content for expandable sidebar Signed-off-by: Philipp Hugenroth --- packages/core-components/src/layout/Sidebar/Bar.tsx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 1770289c08..056a5ae38e 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -23,16 +23,10 @@ import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; import { MobileSidebar } from './MobileSidebar'; -export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; +export type SidebarClassKey = 'drawer' | 'drawerOpen'; const useStyles = makeStyles( theme => ({ - root: { - zIndex: 1000, - position: 'relative', - overflow: 'visible', - width: theme.spacing(7) + 1, - }, drawer: { display: 'flex', flexFlow: 'column nowrap', @@ -41,7 +35,7 @@ const useStyles = makeStyles( left: 0, top: 0, bottom: 0, - padding: 0, + zIndex: 1000, background: theme.palette.navigation.background, overflowX: 'hidden', msOverflowStyle: 'none', From 7ba416be786ea708c01e8a8d302def41e3f79c01 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 12 Nov 2021 18:05:55 +0100 Subject: [PATCH 026/116] Split & improve changesets Fix & cleanup changes related to review Signed-off-by: Philipp Hugenroth --- .changeset/fluffy-countries-beam.md | 10 ++++++++ .changeset/fuzzy-olives-impress.md | 8 ------ .changeset/great-bulldogs-provide.md | 11 ++++++++ .changeset/stale-brooms-fry.md | 5 ++++ packages/core-components/api-report.md | 6 +++-- .../src/layout/Header/Header.tsx | 1 - .../core-components/src/layout/Page/Page.tsx | 25 +++++++++++-------- .../src/layout/Sidebar/Bar.tsx | 7 ++---- .../src/layout/Sidebar/MobileSidebar.tsx | 2 +- .../src/layout/Sidebar/SidebarGroup.tsx | 11 ++++++-- 10 files changed, 56 insertions(+), 30 deletions(-) create mode 100644 .changeset/fluffy-countries-beam.md delete mode 100644 .changeset/fuzzy-olives-impress.md create mode 100644 .changeset/great-bulldogs-provide.md create mode 100644 .changeset/stale-brooms-fry.md diff --git a/.changeset/fluffy-countries-beam.md b/.changeset/fluffy-countries-beam.md new file mode 100644 index 0000000000..145428267b --- /dev/null +++ b/.changeset/fluffy-countries-beam.md @@ -0,0 +1,10 @@ +--- +'@backstage/plugin-catalog': patch +'@backstage/plugin-cost-insights': patch +'@backstage/plugin-shortcuts': patch +'@backstage/plugin-user-settings': patch +--- + +**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore the "Pin Sidebar" setting is removed on mobile screens as the mobile sidebar is always pinned to the bottom. + +**Other plugins:** Smaller style adjustments across plugins to improve the UI on mobile devices. diff --git a/.changeset/fuzzy-olives-impress.md b/.changeset/fuzzy-olives-impress.md deleted file mode 100644 index 9f9c8f755b..0000000000 --- a/.changeset/fuzzy-olives-impress.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -'@backstage/core-components': patch -'@backstage/plugin-catalog': patch -'@backstage/plugin-shortcuts': patch -'@backstage/plugin-user-settings': patch ---- - -Add `MobileSidebar` component & use it for smaller screens to improve the UX on mobile devices by switching to a navigation at the bottom of the screen. For customizing the experience you can group `SidebarItems` in a `SidebarGroup` or create a `SidebarGroup` with a link. For an example take a look at the `Root.tsx` or the updated ["Adding a plugin page to the Sidebar"](https://backstage.io/docs/getting-started/configure-app-with-plugins) documentation. diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md new file mode 100644 index 0000000000..636f9f3380 --- /dev/null +++ b/.changeset/great-bulldogs-provide.md @@ -0,0 +1,11 @@ +--- +'@backstage/core-components': patch +--- + +The `Bar` component will now render a `MobileSidebar` instead of the current sidebar on smaller screens. The state of the `MobileSidebar` will be treated as always open. + +--- + +**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally a `MobileSidebarContext`, which wraps the component, will safe the selected menu item. + +**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens this won't have any effect at the moment. On smaller screens it will render a given icon into the `MobileSidebar`. If a route is provided clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md new file mode 100644 index 0000000000..a3804f13eb --- /dev/null +++ b/.changeset/stale-brooms-fry.md @@ -0,0 +1,5 @@ +--- +'@backstage/create-app': patch +--- + +// TODO diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index a03e8c1447..2fa3315fe1 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -801,7 +801,6 @@ export type SelectInputBaseClassKey = 'root' | 'input'; // @public (undocumented) export const Sidebar: ({ children, - ...props }: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1115,7 +1114,10 @@ export const SidebarDivider: React_2.ComponentType< // @public (undocumented) export const SidebarGroup: ({ children, - ...props + to, + label, + icon, + value, }: React_2.PropsWithChildren) => JSX.Element; // Warning: (ae-missing-release-tag) "SidebarGroupProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/packages/core-components/src/layout/Header/Header.tsx b/packages/core-components/src/layout/Header/Header.tsx index 92f28c1d07..954575a95a 100644 --- a/packages/core-components/src/layout/Header/Header.tsx +++ b/packages/core-components/src/layout/Header/Header.tsx @@ -64,7 +64,6 @@ const useStyles = makeStyles( }, title: { color: theme.palette.bursts.fontColor, - // ? fontSize: 'calc(24px + 6 * ((100vw - 320px) / 680))', marginBottom: 0, }, diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index a0c84bb56a..a1efe4dda9 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -20,17 +20,20 @@ import React, { PropsWithChildren } from 'react'; export type PageClassKey = 'root'; -const useStyles = makeStyles(() => ({ - root: { - display: 'grid', - gridTemplateAreas: - "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", - gridTemplateRows: 'max-content auto 1fr', - gridTemplateColumns: 'auto 1fr auto', - height: '100%', - overflowY: 'auto', - }, -})); +const useStyles = makeStyles( + () => ({ + root: { + display: 'grid', + gridTemplateAreas: + "'pageHeader pageHeader pageHeader' 'pageSubheader pageSubheader pageSubheader' 'pageNav pageContent pageSidebar'", + gridTemplateRows: 'max-content auto 1fr', + gridTemplateColumns: 'auto 1fr auto', + height: '100%', + overflowY: 'auto', + }, + }), + { name: 'BackstagePage' }, +); type Props = { themeId: string; diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 056a5ae38e..db85ca77d7 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -148,10 +148,7 @@ const DesktopSidebar = ({ ); }; -export const Sidebar = ({ - children, - ...props -}: React.PropsWithChildren) => { +export const Sidebar = ({ children }: React.PropsWithChildren) => { const isMobileScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); @@ -162,7 +159,7 @@ export const Sidebar = ({ isOpen: true, }} > - {children} + {children} ) : ( {children} diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 247c191dee..33505987bc 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -124,7 +124,7 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { ), ); - if (!sidebarGroups) { + if (!children) { // If Sidebar has no children the MobileSidebar won't be rendered return null; } else if (!sidebarGroups.length) { diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index d71657ac5a..51ae35c2db 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -88,11 +88,18 @@ const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { export const SidebarGroup = ({ children, - ...props + to, + label, + icon, + value, }: React.PropsWithChildren) => { const isMobileScreen = useMediaQuery(theme => theme.breakpoints.down('xs'), ); - return isMobileScreen ? : <>{children}; + return isMobileScreen ? ( + + ) : ( + <>{children} + ); }; From 4b8cefcf05d5bf125cf6607eb016502df7d6757a Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 15 Nov 2021 11:55:17 +0100 Subject: [PATCH 027/116] Update changeset & documentation Signed-off-by: Philipp Hugenroth --- .changeset/stale-brooms-fry.md | 48 ++++++++++++++++++- app-config.yaml | 4 +- .../configure-app-with-plugins.md | 19 ++++++-- packages/app/src/components/Root/Root.tsx | 2 +- .../src/layout/Sidebar/MobileSidebar.tsx | 2 +- .../packages/app/src/components/Root/Root.tsx | 2 +- 6 files changed, 66 insertions(+), 11 deletions(-) diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md index a3804f13eb..ddd7ba09a6 100644 --- a/.changeset/stale-brooms-fry.md +++ b/.changeset/stale-brooms-fry.md @@ -2,4 +2,50 @@ '@backstage/create-app': patch --- -// TODO +You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaler screens will look like. A `SidebarGroup` wil be displayed with the given icon in the `MobileSidebar`. + +A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full screen overlay menu (e.g. `Menu`). + +```diff + + ++ } to="/search"> + ++ + ++ }> + + + + + + ++ + + ++ } ++ to="/settings" ++ > + ++ + +``` + +Additionally you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in a descending order from left to right. + +```diff +} + to="/settings" ++ priority={1} +> + + +``` + +If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full screen overlay menu displaying all of the content of the current `Sidebar`. + +More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`. diff --git a/app-config.yaml b/app-config.yaml index 8aa3bc569c..2915a67265 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -23,9 +23,9 @@ app: title: '#backstage' backend: - baseUrl: http://localhost:7000 + baseUrl: http://localhost:7001 listen: - port: 7000 + port: 7001 database: client: sqlite3 connection: ':memory:' diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 11e2f4d75a..7f522437da 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -92,12 +92,12 @@ import InternalToolIcon from './internal-tool.icon.svg'; ``` On mobile devices the `Sidebar` is displayed at the bottom of the screen. For -customizing the experience you can group `SidebarItems` in a `SidebarGroup` or -create a `SidebarGroup` with a link. All `SidebarGroups` are displayed in the -bottom navigation with an icon. +customizing the experience you can group `SidebarItems` in a `SidebarGroup` +(Example 1) or create a `SidebarGroup` with a link (Example 2). All +`SidebarGroups` are displayed in the bottom navigation with an icon. ```ts -// ... inside the AppSidebar component +// Example 1 } label="Menu"> ... @@ -105,5 +105,14 @@ bottom navigation with an icon. ``` -If no `SidebarGroup`is provided a default menu will display the `Sidebar` +```ts +// Example 2 +} to="/search"> + ... + + ... + +``` + +If no `SidebarGroup` is provided a default menu will display the `Sidebar` content. diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 5412412126..87fa8c8470 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -89,7 +89,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 33505987bc..d7817f3037 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -73,7 +73,7 @@ const useStyles = makeStyles(theme => ({ const sortSidebarGroupsForPriority = (children: React.ReactElement[]) => orderBy( children, - ({ props: { priority } }) => (priority ? priority : -1), + ({ props: { priority } }) => (Number.isInteger(priority) ? priority : -1), 'desc', ); diff --git a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx index 23670dcb63..c540ce7f59 100644 --- a/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx +++ b/packages/create-app/templates/default-app/packages/app/src/components/Root/Root.tsx @@ -84,7 +84,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - }> + }> {/* Global nav, not org-specific */} From 066805f9995a5a85a131ee12b7302b3f48c48ad2 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 15 Nov 2021 13:12:39 +0100 Subject: [PATCH 028/116] Spellchecking for changesets Signed-off-by: Philipp Hugenroth --- .changeset/fluffy-countries-beam.md | 2 +- .changeset/great-bulldogs-provide.md | 4 ++-- .changeset/stale-brooms-fry.md | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.changeset/fluffy-countries-beam.md b/.changeset/fluffy-countries-beam.md index 145428267b..f542cfd33b 100644 --- a/.changeset/fluffy-countries-beam.md +++ b/.changeset/fluffy-countries-beam.md @@ -5,6 +5,6 @@ '@backstage/plugin-user-settings': patch --- -**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore the "Pin Sidebar" setting is removed on mobile screens as the mobile sidebar is always pinned to the bottom. +**@backstage/plugin-user-settings:** Hide Header on mobile screens to improve the UI & give more space to the content. Furthermore, the "Pin Sidebar" setting is removed on mobile screens, as the mobile sidebar is always pinned to the bottom. **Other plugins:** Smaller style adjustments across plugins to improve the UI on mobile devices. diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md index 636f9f3380..f076dfa545 100644 --- a/.changeset/great-bulldogs-provide.md +++ b/.changeset/great-bulldogs-provide.md @@ -6,6 +6,6 @@ The `Bar` component will now render a `MobileSidebar` instead of the current sid --- -**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally a `MobileSidebarContext`, which wraps the component, will safe the selected menu item. +**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item. -**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens this won't have any effect at the moment. On smaller screens it will render a given icon into the `MobileSidebar`. If a route is provided clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. +**Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens, this won't have any effect at the moment. On smaller screens, it will render a given icon into the `MobileSidebar`. If a route is provided, clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided, it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md index ddd7ba09a6..c98a427e80 100644 --- a/.changeset/stale-brooms-fry.md +++ b/.changeset/stale-brooms-fry.md @@ -2,9 +2,9 @@ '@backstage/create-app': patch --- -You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaler screens will look like. A `SidebarGroup` wil be displayed with the given icon in the `MobileSidebar`. +You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`. -A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full screen overlay menu (e.g. `Menu`). +A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full-screen overlay menu (e.g. `Menu`). ```diff @@ -33,7 +33,7 @@ A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settin ``` -Additionally you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in a descending order from left to right. +Additionally, you can order the groups differently in the `MobileSidebar` than in the usual `Sidebar` simply by giving a group a priority. The groups will be displayed in descending order from left to right. ```diff ``` -If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full screen overlay menu displaying all of the content of the current `Sidebar`. +If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`. More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`. From 9a0e4ffe88c1b42a5aae37d0e44d39248e19293c Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 15 Nov 2021 13:28:35 +0100 Subject: [PATCH 029/116] Update app-config.yaml Signed-off-by: Philipp Hugenroth --- app-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-config.yaml b/app-config.yaml index 2915a67265..8aa3bc569c 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -23,9 +23,9 @@ app: title: '#backstage' backend: - baseUrl: http://localhost:7001 + baseUrl: http://localhost:7000 listen: - port: 7001 + port: 7000 database: client: sqlite3 connection: ':memory:' From 7d9913fc43b24eb5afb9c0383fb1b80ad9af26d9 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 7 Dec 2021 16:49:24 +0100 Subject: [PATCH 030/116] Add basic support for submenus to mobile sidebar Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 13 +++++--- .../src/layout/Sidebar/Bar.tsx | 20 +++-------- .../src/layout/Sidebar/Items.tsx | 31 +++-------------- .../src/layout/Sidebar/MobileSidebar.tsx | 33 ++++++++++--------- .../src/layout/Sidebar/SidebarSubmenu.tsx | 8 +++++ .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 4 +++ .../src/layout/Sidebar/config.ts | 3 +- 7 files changed, 50 insertions(+), 62 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 63cd694d28..a5f2010758 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -809,6 +809,11 @@ export const Sidebar: ({ export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; +// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export type SidebarClassKey = 'drawer' | 'drawerOpen'; + // Warning: (ae-missing-release-tag) "sidebarConfig" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -838,7 +843,7 @@ export const SidebarContext: Context; // @public (undocumented) export type SidebarContextType = { isOpen: boolean; - setOpen: (open: boolean) => void; + setOpen?: (open: boolean) => void; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1110,6 +1115,9 @@ export const SidebarDivider: React_2.ComponentType< } >; +// @public +export const SidebarExpandButton: () => JSX.Element | null; + // Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -1131,9 +1139,6 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; } -// @public -export const SidebarExpandButton: () => JSX.Element | null; - // Warning: (ae-missing-release-tag) "SidebarIntro" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index f1d1b73be4..647495a6f1 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -178,18 +178,8 @@ export const Sidebar = ({ children }: React.PropsWithChildren) => { theme.breakpoints.down('xs'), ); - // TODO: Generalize - const setOpen = () => {}; - return isMobileScreen ? ( - - {children} - + {children} ) : ( {children} ); @@ -209,14 +199,14 @@ export const SidebarExpandButton = () => { theme.breakpoints.down('md'), ); + if (isPinned || isSmallScreen || !setOpen) { + return null; + } + const handleClick = () => { setOpen(!isOpen); }; - if (isPinned || isSmallScreen) { - return null; - } - return (
); - const closedContent = itemIcon; return ( -
+
- {isOpen ? openContent : closedContent} + {isOpen ? openContent : itemIcon} {!isHoveredOn && ( )} @@ -488,28 +483,12 @@ export const SidebarItem = forwardRef((props, ref) => { ), }; - let hasSubmenu = false; - let submenu: ReactNode; - const componentType = ( - - <> - - ).type; // Filter children for SidebarSubmenu components - const submenus = useElementFilter(children, elements => - elements.getElements().filter(child => child.type === componentType), + const [submenu] = useElementFilter(children, elements => + elements.getElements().filter(child => child.type === SidebarSubmenu), ); - // Error thrown if more than one SidebarSubmenu in a SidebarItem - if (submenus.length > 1) { - throw new Error( - 'Cannot render more than one SidebarSubmenu inside a SidebarItem', - ); - } else if (submenus.length === 1) { - hasSubmenu = true; - submenu = submenus[0]; - } - if (hasSubmenu) { + if (submenu) { return ( ) => { !sidebarGroups[selectedMenuItemIndex].props.to; return ( - - {shouldShowGroupChildren && ( - setSelectedMenuItemIndex(-1)} - /> - )} - + - {sidebarGroups} - - + {shouldShowGroupChildren && ( + setSelectedMenuItemIndex(-1)} + /> + )} + + {sidebarGroups} + + + ); }; diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx index 51324980fe..7c4a554e4d 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx @@ -58,12 +58,20 @@ const useStyles = (props: { left: number }) => }, drawerOpen: { width: submenuConfig.drawerWidthOpen, + [theme.breakpoints.down('sm')]: { + width: '100%', + position: 'relative', + paddingLeft: theme.spacing(3), + left: 0, + top: 0, + }, }, title: { fontSize: 24, fontWeight: 500, color: '#FFF', padding: 20, + display: 'none', }, })); diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 2bb8a13592..3d676ff83a 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -76,6 +76,10 @@ const useStyles = makeStyles(theme => ({ color: theme.palette.navigation.color, display: 'flex', justifyContent: 'center', + [theme.breakpoints.down('xs')]: { + display: 'block', + paddingLeft: theme.spacing(4), + }, fontSize: '14px', }, })); diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index 9aa2a25d05..a26a3dbc0d 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -60,12 +60,11 @@ export const SIDEBAR_INTRO_LOCAL_STORAGE = export type SidebarContextType = { isOpen: boolean; - setOpen: (open: boolean) => void; + setOpen?: (open: boolean) => void; }; export const SidebarContext = createContext({ isOpen: false, - setOpen: _open => {}, }); export type SidebarItemWithSubmenuContextType = { From 510a5953979db37e13461d145d34913b33dcfa20 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 9 Dec 2021 10:53:17 +0100 Subject: [PATCH 031/116] Improve mobile submenu experience Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/Items.tsx | 34 ++++++++++++++++--- .../src/layout/Sidebar/SidebarSubmenu.tsx | 12 +++++-- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 9 +++++ 3 files changed, 48 insertions(+), 7 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 8c0e175c6c..d7bea9876d 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -17,6 +17,7 @@ import { IconComponent, useElementFilter } from '@backstage/core-plugin-api'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, styled, Theme } from '@material-ui/core/styles'; +import useMediaQuery from '@material-ui/core/useMediaQuery'; import Badge from '@material-ui/core/Badge'; import TextField from '@material-ui/core/TextField'; import Typography from '@material-ui/core/Typography'; @@ -46,6 +47,8 @@ import { SidebarItemWithSubmenuContext, } from './config'; import { SidebarSubmenu } from './SidebarSubmenu'; +import ArrowDropUp from '@material-ui/icons/ArrowDropUp'; +import ArrowDropDown from '@material-ui/icons/ArrowDropDown'; export type SidebarItemClassKey = | 'root' @@ -151,6 +154,9 @@ const useStyles = makeStyles( submenuArrow: { position: 'absolute', right: 0, + [theme.breakpoints.down('xs')]: { + right: theme.spacing(2), + }, }, selected: { '&$root': { @@ -274,6 +280,9 @@ const SidebarItemWithSubmenu = ({ const [isHoveredOn, setIsHoveredOn] = useState(false); const { pathname: locationPathname } = useLocation(); const isActive = isSidebarItemWithSubmenuActive(children, locationPathname); + const isSmallScreen = useMediaQuery((theme: BackstageTheme) => + theme.breakpoints.down('sm'), + ); const handleMouseEnter = () => { setIsHoveredOn(true); @@ -308,6 +317,21 @@ const SidebarItemWithSubmenu = ({ ); + const arrowIcon = () => { + if (isSmallScreen) { + return isHoveredOn ? ( + + ) : ( + + ); + } + return ( + !isHoveredOn && ( + + ) + ); + }; + return ( -
+
{isOpen ? openContent : itemIcon} - {!isHoveredOn && ( - - )} + {arrowIcon()}
{isHoveredOn && children}
diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx index 7c4a554e4d..dfa895df71 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenu.tsx @@ -38,7 +38,13 @@ const useStyles = (props: { left: number }) => flexFlow: 'column nowrap', alignItems: 'flex-start', position: 'fixed', - left: props.left, + [theme.breakpoints.up('sm')]: { + marginLeft: props.left, + transition: theme.transitions.create('margin-left', { + easing: theme.transitions.easing.sharp, + duration: theme.transitions.duration.shortest, + }), + }, top: 0, bottom: 0, padding: 0, @@ -58,7 +64,7 @@ const useStyles = (props: { left: number }) => }, drawerOpen: { width: submenuConfig.drawerWidthOpen, - [theme.breakpoints.down('sm')]: { + [theme.breakpoints.down('xs')]: { width: '100%', position: 'relative', paddingLeft: theme.spacing(3), @@ -99,7 +105,7 @@ export const SidebarSubmenu = ({ const left = isOpen ? sidebarConfig.drawerWidthOpen : sidebarConfig.drawerWidthClosed; - const props = { left: left }; + const props = { left }; const classes = useStyles(props)(); const { isHoveredOn } = useContext(SidebarItemWithSubmenuContext); diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 3d676ff83a..60dc591d92 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -29,6 +29,7 @@ import { BackstageTheme } from '@backstage/theme'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; +import { SidebarContext } from '../..'; const useStyles = makeStyles(theme => ({ item: { @@ -123,8 +124,13 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { pathname: locationPathname } = useLocation(); const { pathname: toPathname } = useResolvedPath(to); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); + const { setOpen } = useContext(SidebarContext); + const closeSubmenu = () => { setIsHoveredOn(false); + if (setOpen) { + setOpen(false); + } }; let isActive = locationPathname === toPathname; @@ -142,6 +148,7 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => {
- ); -}; diff --git a/packages/core-components/src/layout/Sidebar/Items.test.tsx b/packages/core-components/src/layout/Sidebar/Items.test.tsx index f4d699d09c..b2cabd03b8 100644 --- a/packages/core-components/src/layout/Sidebar/Items.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.test.tsx @@ -20,8 +20,8 @@ import { createEvent, fireEvent, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import HomeIcon from '@material-ui/icons/Home'; import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import { Sidebar, SidebarExpandButton } from './Bar'; -import { SidebarItem, SidebarSearchField } from './Items'; +import { Sidebar } from './Bar'; +import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items'; import { renderHook } from '@testing-library/react-hooks'; import { hexToRgb, makeStyles } from '@material-ui/core/styles'; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 0aed75460e..0b1d15d4b8 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -50,8 +50,8 @@ import { SidebarSubmenuProps, SidebarSubmenu, } from '.'; -import { isLocationMatch } from './utils'; -import { Location } from 'history'; +import DoubleArrowLeft from './icons/DoubleArrowLeft'; +import DoubleArrowRight from './icons/DoubleArrowRight'; export type SidebarItemClassKey = | 'root' @@ -157,6 +157,19 @@ const useStyles = makeStyles( submenuArrow: { display: 'flex', }, + expandButton: { + background: 'none', + border: 'none', + color: theme.palette.navigation.color, + width: '100%', + cursor: 'pointer', + position: 'relative', + height: 48, + }, + arrows: { + position: 'absolute', + right: 10, + }, selected: { '&$root': { borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, @@ -706,3 +719,39 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => { '&:hover': scrollbarStyles, }; }); + +/** + * A button which allows you to expand the sidebar when clicked. + * Use optionally to replace sidebar's expand-on-hover feature with expand-on-click. + * + * @public + */ +export const SidebarExpandButton = () => { + const classes = useStyles(); + const { isOpen, setOpen } = useContext(SidebarContext); + const isSmallScreen = useMediaQuery( + theme => theme.breakpoints.down('md'), + { noSsr: true }, + ); + + if (isSmallScreen || !setOpen) { + return null; + } + + const handleClick = () => { + setOpen(!isOpen); + }; + + return ( + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index e94d245266..47aba5e18e 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -29,7 +29,6 @@ import { BackstageTheme } from '@backstage/theme'; import ArrowDropDownIcon from '@material-ui/icons/ArrowDropDown'; import ArrowDropUpIcon from '@material-ui/icons/ArrowDropUp'; import { SidebarItemWithSubmenuContext } from './config'; -import { SidebarContext } from '../..'; import { isLocationMatch } from './utils'; const useStyles = makeStyles(theme => ({ @@ -123,13 +122,9 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { title, to, icon: Icon, dropdownItems } = props; const classes = useStyles(); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); - const { setOpen } = useContext(SidebarContext); const closeSubmenu = () => { setIsHoveredOn(false); - if (setOpen) { - setOpen(false); - } }; const toLocation = useResolvedPath(to); const currentLocation = useLocation(); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index b0b8ca70a5..2b5ff77538 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -export { Sidebar, SidebarExpandButton } from './Bar'; +export { Sidebar } from './Bar'; export { MobileSidebar } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; @@ -35,6 +35,7 @@ export { SidebarSpace, SidebarSpacer, SidebarScrollWrapper, + SidebarExpandButton, } from './Items'; export type { SidebarItemClassKey, From 3df39d8ed3710bc4212d7e1f3ea1e83fa4202206 Mon Sep 17 00:00:00 2001 From: Nigel Wright Date: Tue, 28 Dec 2021 13:25:05 +1300 Subject: [PATCH 037/116] added additional information for s3 discovery usage Signed-off-by: Nigel Wright --- docs/integrations/aws-s3/discovery.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md index d50aceacb8..656cb9f7b3 100644 --- a/docs/integrations/aws-s3/discovery.md +++ b/docs/integrations/aws-s3/discovery.md @@ -26,3 +26,16 @@ catalog: ``` Note the `s3-discovery` type, as this is not a regular `url` processor. + +As this processor is not one of the default providers, you will also need to add the below: + +```javascript +/* packages/backend/src/plugins/catalog.ts */ + +import { AwsS3DiscoveryProcessor} from '@backstage/plugin-catalog-backend'; + + +const builder = await CatalogBuilder.create(env); +/** ... other processors ... */ +builder.addProcessor(new AwsS3DiscoveryProcessor(env.reader)) +``` From 545a26a0b675e48647c3218f3638f6dbd33a9c41 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Tue, 28 Dec 2021 18:06:23 +0100 Subject: [PATCH 038/116] Move 'isMobile' in Context Optimize generated docs by labeling components with scope Signed-off-by: Philipp Hugenroth --- .../app/src/components/search/SearchPage.tsx | 30 +++--- packages/core-components/api-report.md | 66 ++++++------- .../src/layout/Sidebar/Bar.test.tsx | 12 ++- .../src/layout/Sidebar/Bar.tsx | 28 +++--- .../src/layout/Sidebar/Items.tsx | 93 +++---------------- .../src/layout/Sidebar/MobileSidebar.tsx | 49 +++++++--- .../src/layout/Sidebar/Page.tsx | 27 ++++-- .../src/layout/Sidebar/Sidebar.stories.tsx | 1 - .../src/layout/Sidebar/SidebarGroup.tsx | 26 +++--- .../src/layout/Sidebar/index.ts | 12 ++- .../General/UserSettingsAppearanceCard.tsx | 13 +-- .../General/UserSettingsPinToggle.test.tsx | 12 ++- .../General/UserSettingsPinToggle.tsx | 6 +- .../src/components/SettingsPage.tsx | 17 ++-- 14 files changed, 166 insertions(+), 226 deletions(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 2a21ad0e86..a734b72142 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -14,7 +14,13 @@ * limitations under the License. */ -import { Content, Header, Lifecycle, Page } from '@backstage/core-components'; +import { + Content, + Header, + Lifecycle, + Page, + SidebarStateContext, +} from '@backstage/core-components'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; import { DefaultResultListItem, @@ -25,16 +31,8 @@ import { SearchType, } from '@backstage/plugin-search'; import { DocsResultListItem } from '@backstage/plugin-techdocs'; -import { - Grid, - List, - makeStyles, - Paper, - Theme, - useMediaQuery, -} from '@material-ui/core'; -import React from 'react'; -import { BackstageTheme } from '@backstage/theme'; +import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core'; +import React, { useContext } from 'react'; const useStyles = makeStyles((theme: Theme) => ({ bar: { @@ -52,15 +50,11 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); + const { isMobile } = useContext(SidebarStateContext); return ( - {!isMobileScreen && ( -
} /> - )} + {!isMobile &&
} />} @@ -68,7 +62,7 @@ const SearchPage = () => { - {!isMobileScreen && ( + {!isMobile && ( ) => JSX.Element | null; +export const MobileSidebar: ( + props: React_2.PropsWithChildren<{}>, +) => JSX.Element | null; // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -843,16 +841,10 @@ export type SelectClassKey = // @public (undocumented) export type SelectInputBaseClassKey = 'root' | 'input'; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "Sidebar" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) -export const Sidebar: ({ - children, - openDelayMs, - closeDelayMs, - disableExpandOnHover, -}: React_2.PropsWithChildren) => JSX.Element; +export const Sidebar: ( + props: React_2.PropsWithChildren, +) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -860,8 +852,6 @@ export const Sidebar: ({ export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; -// Warning: (ae-missing-release-tag) "SidebarClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type SidebarClassKey = 'drawer' | 'drawerOpen'; @@ -1174,19 +1164,11 @@ export type SidebarDividerClassKey = 'root'; // @public export const SidebarExpandButton: () => JSX.Element | null; -// Warning: (ae-missing-release-tag) "SidebarGroup" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) -export const SidebarGroup: ({ - children, - to, - label, - icon, - value, -}: React_2.PropsWithChildren) => JSX.Element; +export const SidebarGroup: ( + props: React_2.PropsWithChildren, +) => JSX.Element; -// Warning: (ae-missing-release-tag) "SidebarGroupProps" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export interface SidebarGroupProps extends BottomNavigationActionProps { // (undocumented) @@ -1243,17 +1225,11 @@ export function SidebarPage(props: PropsWithChildren<{}>): JSX.Element; // @public (undocumented) export type SidebarPageClassKey = 'root'; -// Warning: (ae-missing-release-tag) "SidebarPinStateContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) -export const SidebarPinStateContext: React_2.Context; - -// Warning: (ae-missing-release-tag) "SidebarPinStateContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export type SidebarPinStateContextType = { - isPinned: boolean; - toggleSidebarPinState: () => any; +export type SidebarProps = { + openDelayMs?: number; + closeDelayMs?: number; + disableExpandOnHover?: boolean; }; // Warning: (ae-missing-release-tag) "SidebarScrollWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2079,6 +2055,16 @@ export const SidebarSpacer: React_2.ComponentType< // @public (undocumented) export type SidebarSpacerClassKey = 'root'; +// @public (undocumented) +export const SidebarStateContext: React_2.Context; + +// @public (undocumented) +export type SidebarStateContextType = { + isPinned: boolean; + toggleSidebarPinState: () => any; + isMobile: boolean; +}; + // @public export const SidebarSubmenu: (props: SidebarSubmenuProps) => JSX.Element; @@ -2111,7 +2097,7 @@ export type SidebarSubmenuProps = { // Warning: (ae-missing-release-tag) "SignInPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function SignInPage(props: Props_18): JSX.Element; +export function SignInPage(props: Props_17): JSX.Element; // Warning: (ae-missing-release-tag) "SignInPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2282,7 +2268,7 @@ export type TabBarClassKey = 'indicator' | 'flexContainer' | 'root'; // Warning: (ae-missing-release-tag) "TabbedCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function TabbedCard(props: PropsWithChildren): JSX.Element; +export function TabbedCard(props: PropsWithChildren): JSX.Element; // Warning: (ae-missing-release-tag) "TabbedCardClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index 11b4088dc0..acfd949ac5 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -26,12 +26,16 @@ import { Sidebar } from './Bar'; import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items'; import { SidebarSubmenuItem } from './SidebarSubmenuItem'; import { SidebarSubmenu } from './SidebarSubmenu'; -import { SidebarPinStateContext } from '.'; +import { SidebarStateContext } from '.'; async function renderScalableSidebar() { await renderInTestApp( - {} }} + {}, + }} > {}} to="/search" /> @@ -58,7 +62,7 @@ async function renderScalableSidebar() { - , + , ); } diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 96f7335acd..51d4268d05 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -20,9 +20,10 @@ import classnames from 'classnames'; import React, { useState, useContext, PropsWithChildren, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarPinStateContext } from './Page'; +import { SidebarStateContext } from './Page'; import { MobileSidebar } from './MobileSidebar'; +/** @public */ export type SidebarClassKey = 'drawer' | 'drawerOpen'; const useStyles = makeStyles( @@ -69,7 +70,8 @@ enum State { Open, } -type Props = { +/** @public */ +export type SidebarProps = { openDelayMs?: number; closeDelayMs?: number; disableExpandOnHover?: boolean; @@ -80,7 +82,7 @@ const DesktopSidebar = ({ closeDelayMs = sidebarConfig.defaultCloseDelayMs, disableExpandOnHover, children, -}: PropsWithChildren) => { +}: PropsWithChildren) => { const classes = useStyles(); const isSmallScreen = useMediaQuery( theme => theme.breakpoints.down('md'), @@ -88,9 +90,7 @@ const DesktopSidebar = ({ ); const [state, setState] = useState(State.Closed); const hoverTimerRef = useRef(); - const { isPinned, toggleSidebarPinState } = useContext( - SidebarPinStateContext, - ); + const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); const handleOpen = () => { if (isPinned || disableExpandOnHover) { @@ -163,18 +163,12 @@ const DesktopSidebar = ({ ); }; -export const Sidebar = ({ - children, - openDelayMs, - closeDelayMs, - disableExpandOnHover, -}: React.PropsWithChildren) => { - const isMobileScreen = useMediaQuery( - theme => theme.breakpoints.down('xs'), - { noSsr: true }, - ); +/** @public */ +export const Sidebar = (props: React.PropsWithChildren) => { + const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; + const { isMobile } = useContext(SidebarStateContext); - return isMobileScreen ? ( + return isMobile ? ( {children} ) : ( ( fontSize: theme.typography.fontSize, }, searchFieldHTMLInput: { - padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + padding: theme.spacing(2, 0, 2), }, searchContainer: { width: drawerWidthOpen - iconContainerWidth, @@ -219,7 +222,7 @@ const useStyles = makeStyles( fontSize: theme.typography.fontSize, }, searchFieldHTMLInput: { - padding: `${theme.spacing(2)} 0 ${theme.spacing(2)}`, + padding: theme.spacing(2, 0, 2), }, searchContainer: { width: drawerWidthOpen - iconContainerWidth, @@ -237,7 +240,7 @@ const useStyles = makeStyles( const useLocationMatch = ( submenu: React.ReactElement, - locationPathname: string, + location: Location, ): boolean => // Evaluates the routes of the SubmenuItems & nested DropdownItems. // The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization @@ -257,93 +260,21 @@ const useLocationMatch = ( if (dropdownItems?.length) { dropdownItems.forEach( ({ to: _to }) => - (active = active || locationPathname.includes(_to)), + (active = + active || isLocationMatch(location, resolvePath(_to))), ); return; } if (to) { - active = locationPathname.includes(to); + active = isLocationMatch(location, resolvePath(to)); } } }, ); return active; }, - [locationPathname], + [location.pathname], ); -/* -function isSidebarItemWithSubmenuActive( - submenu: ReactNode, - currentLocation: Location, -) { - // Item is active if any of submenu items have active paths - const toPathnames: string[] = []; - let isActive = false; - let submenuItems: ReactNode; - Children.forEach(submenu, element => { - if (!React.isValidElement(element)) return; - submenuItems = element.props.children; - }); - Children.forEach(submenuItems, element => { - if (!React.isValidElement(element)) return; - if (element.props.dropdownItems) { - element.props.dropdownItems.map((item: { to: string }) => - toPathnames.push(item.to), - ); - } else if (element.props.to) { - toPathnames.push(element.props.to); - } - }); - isActive = toPathnames.some(to => { - const toLocation = resolvePath(to); - return isLocationMatch(currentLocation, toLocation); - }); - return isActive; -} - -const SidebarItemWithSubmenu = ({ - text, - hasNotifications = false, - icon: Icon, - children, -}: PropsWithChildren) => { - const classes = useStyles(); - const [isHoveredOn, setIsHoveredOn] = useState(false); - const currentLocation = useLocation(); - const isActive = isSidebarItemWithSubmenuActive(children, currentLocation); - - const handleMouseEnter = () => { - setIsHoveredOn(true); - }; - const handleMouseLeave = () => { - setIsHoveredOn(false); - }; - - const { isOpen } = useContext(SidebarContext); - const itemIcon = ( - - - - ); - const openContent = ( - <> -
- {itemIcon} -
- {text && ( - - {text} - - )} -
{}
- - );*/ type SidebarItemBaseProps = { icon: IconComponent; @@ -522,8 +453,8 @@ const SidebarItemWithSubmenu = ({ }) => { const classes = useStyles(); const [isHoveredOn, setIsHoveredOn] = useState(false); - const { pathname: locationPathname } = useLocation(); - const isActive = useLocationMatch(children, locationPathname); + const location = useLocation(); + const isActive = useLocationMatch(children, location); const isSmallScreen = useMediaQuery((theme: BackstageTheme) => theme.breakpoints.down('sm'), ); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 6ee2451755..158b20e6be 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -20,6 +20,7 @@ import BottomNavigation from '@material-ui/core/BottomNavigation'; import Box from '@material-ui/core/Box'; import IconButton from '@material-ui/core/IconButton'; import { makeStyles } from '@material-ui/core/styles'; +import Drawer from '@material-ui/core/Drawer'; import Typography from '@material-ui/core/Typography'; import CloseIcon from '@material-ui/icons/Close'; import MenuIcon from '@material-ui/icons/Menu'; @@ -43,7 +44,7 @@ const useStyles = makeStyles(theme => ({ bottom: 0, left: 0, right: 0, - zIndex: 1000, + zIndex: theme.zIndex.snackbar, // SidebarDivider color borderTop: '1px solid #383838', }, @@ -51,11 +52,10 @@ const useStyles = makeStyles(theme => ({ overlay: { background: theme.palette.navigation.background, width: '100%', - flex: '0 1 auto', + bottom: `${sidebarConfig.mobileSidebarHeight}px`, height: `calc(100% - ${sidebarConfig.mobileSidebarHeight}px)`, + flex: '0 1 auto', overflow: 'auto', - position: 'fixed', - zIndex: 500, }, overlayHeader: { @@ -63,7 +63,7 @@ const useStyles = makeStyles(theme => ({ color: theme.palette.bursts.fontColor, alignItems: 'center', justifyContent: 'space-between', - padding: `${theme.spacing(2)}px ${theme.spacing(3)}px`, + padding: theme.spacing(2, 3), }, overlayHeaderClose: { @@ -81,12 +81,22 @@ const sortSidebarGroupsForPriority = (children: React.ReactElement[]) => const OverlayMenu = ({ children, label = 'Menu', + open, onClose, -}: React.PropsWithChildren<{ label?: string; onClose: () => void }>) => { +}: React.PropsWithChildren<{ + label?: string; + onClose: () => void; + open: boolean; +}>) => { const classes = useStyles(); return ( - + {label} {children} - + ); }; @@ -106,7 +116,9 @@ export const MobileSidebarContext = createContext({ setSelectedMenuItemIndex: () => {}, }); -export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { +/** @public */ +export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { + const { children } = props; const classes = useStyles(); const location = useLocation(); const [selectedMenuItemIndex, setSelectedMenuItemIndex] = @@ -146,12 +158,19 @@ export const MobileSidebar = ({ children }: React.PropsWithChildren<{}>) => { - {shouldShowGroupChildren && ( - setSelectedMenuItemIndex(-1)} - /> - )} + setSelectedMenuItemIndex(-1)} + > + {sidebarGroups[selectedMenuItemIndex] && + (sidebarGroups[selectedMenuItemIndex].props + .children as React.ReactChildren)} + ( { name: 'BackstageSidebarPage' }, ); -export type SidebarPinStateContextType = { +/** @public */ +export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; + isMobile: boolean; }; -export const SidebarPinStateContext = createContext( - { - isPinned: true, - toggleSidebarPinState: () => {}, - }, -); +/** @public */ +export const SidebarStateContext = createContext({ + isPinned: true, + toggleSidebarPinState: () => {}, + isMobile: false, +}); export function SidebarPage(props: PropsWithChildren<{}>) { const [isPinned, setIsPinned] = useState(() => @@ -67,17 +70,23 @@ export function SidebarPage(props: PropsWithChildren<{}>) { LocalStorage.setSidebarPinState(isPinned); }, [isPinned]); + const isMobile = useMediaQuery( + theme => theme.breakpoints.down('xs'), + { noSsr: true }, + ); + const toggleSidebarPinState = () => setIsPinned(!isPinned); const classes = useStyles({ isPinned }); return ( -
{props.children}
-
+ ); } diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 0636cac134..9b5d76a32c 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -108,6 +108,5 @@ export const SampleScalableSidebar = () => ( - Test ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 51ae35c2db..1a43f703bf 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -20,13 +20,14 @@ import BottomNavigationAction, { BottomNavigationActionProps, } from '@material-ui/core/BottomNavigationAction'; import { makeStyles } from '@material-ui/core/styles'; -import useMediaQuery from '@material-ui/core/useMediaQuery'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; +import { SidebarStateContext } from '.'; import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; +/** @public */ export interface SidebarGroupProps extends BottomNavigationActionProps { to?: string; priority?: number; @@ -35,7 +36,7 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { const useStyles = makeStyles(theme => ({ root: { flexGrow: 0, - margin: `0 ${theme.spacing(2)}px`, + margin: theme.spacing(0, 2), color: theme.palette.navigation.color, }, @@ -50,7 +51,8 @@ const useStyles = makeStyles(theme => ({ }, })); -const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { +const MobileSidebarGroup = (props: SidebarGroupProps) => { + const { to, label, icon, value } = props; const classes = useStyles(); const location = useLocation(); const { selectedMenuItemIndex, setSelectedMenuItemIndex } = @@ -86,18 +88,14 @@ const MobileSidebarGroup = ({ to, label, icon, value }: SidebarGroupProps) => { ); }; -export const SidebarGroup = ({ - children, - to, - label, - icon, - value, -}: React.PropsWithChildren) => { - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); +/** @public */ +export const SidebarGroup = ( + props: React.PropsWithChildren, +) => { + const { children, to, label, icon, value } = props; + const { isMobile } = useContext(SidebarStateContext); - return isMobileScreen ? ( + return isMobile ? ( ) : ( <>{children} diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 2b5ff77538..4a1e1c0474 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -25,9 +25,15 @@ export type { SidebarSubmenuItemProps, SidebarSubmenuItemDropdownItem, } from './SidebarSubmenuItem'; -export type { SidebarClassKey } from './Bar'; -export { SidebarPage, SidebarPinStateContext } from './Page'; -export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; +export type { SidebarClassKey, SidebarProps } from './Bar'; +export { + SidebarPage, + SidebarStateContext as SidebarStateContext, +} from './Page'; +export type { + SidebarStateContextType as SidebarStateContextType, + SidebarPageClassKey, +} from './Page'; export { SidebarDivider, SidebarItem, diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 83937f9300..915edc94cb 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,23 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InfoCard } from '@backstage/core-components'; -import { BackstageTheme } from '@backstage/theme'; -import { List, useMediaQuery } from '@material-ui/core'; -import React from 'react'; +import { InfoCard, SidebarStateContext } from '@backstage/core-components'; +import { List } from '@material-ui/core'; +import React, { useContext } from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); + const isMobile = useContext(SidebarStateContext); return ( - {!isMobileScreen && } + {!isMobile && } ); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx index 39387df907..e2c9f23b91 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx @@ -18,18 +18,22 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarStateContext } from '@backstage/core-components'; describe('', () => { it('toggles the pin sidebar button', async () => { const mockToggleFn = jest.fn(); const rendered = await renderWithEffects( wrapInTestApp( - - , + , ), ); expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx index 4d71df8113..1cfd8b06dd 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx @@ -22,12 +22,10 @@ import { Switch, Tooltip, } from '@material-ui/core'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarStateContext } from '@backstage/core-components'; export const UserSettingsPinToggle = () => { - const { isPinned, toggleSidebarPinState } = useContext( - SidebarPinStateContext, - ); + const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); return ( diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index d5c7171148..b1bfdb5fc9 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -14,10 +14,13 @@ * limitations under the License. */ -import { Header, Page, TabbedLayout } from '@backstage/core-components'; -import { BackstageTheme } from '@backstage/theme'; -import { useMediaQuery } from '@material-ui/core'; -import React from 'react'; +import { + Header, + Page, + SidebarStateContext, + TabbedLayout, +} from '@backstage/core-components'; +import React, { useContext } from 'react'; import { UserSettingsAuthProviders } from './AuthProviders'; import { UserSettingsFeatureFlags } from './FeatureFlags'; import { UserSettingsGeneral } from './General'; @@ -27,13 +30,11 @@ type Props = { }; export const SettingsPage = ({ providerSettings }: Props) => { - const isMobileScreen = useMediaQuery(theme => - theme.breakpoints.down('xs'), - ); + const { isMobile } = useContext(SidebarStateContext); return ( - {!isMobileScreen &&
} + {!isMobile &&
} From 02338a65157fe16d6e9c8710cd5e91aafeae1a4f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 29 Dec 2021 18:05:17 +0100 Subject: [PATCH 039/116] Improve documentation - Fix tests Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 37 +++++++----- .../core-components/src/layout/Page/Page.tsx | 2 +- .../src/layout/Sidebar/Bar.test.tsx | 26 +++++---- .../src/layout/Sidebar/Bar.tsx | 34 ++++++++--- .../src/layout/Sidebar/Items.tsx | 29 +++++++++- .../src/layout/Sidebar/MobileSidebar.test.tsx | 57 +++++++++++-------- .../src/layout/Sidebar/MobileSidebar.tsx | 27 +++++++-- .../src/layout/Sidebar/Page.tsx | 12 +++- .../src/layout/Sidebar/SidebarGroup.test.tsx | 38 ++++++++----- .../src/layout/Sidebar/SidebarGroup.tsx | 29 +++++++++- .../src/layout/Sidebar/SidebarSubmenuItem.tsx | 1 - .../src/layout/Sidebar/config.ts | 6 ++ .../src/layout/Sidebar/index.ts | 3 +- .../src/overridableComponents.ts | 4 ++ .../General/UserSettingsAppearanceCard.tsx | 2 +- 15 files changed, 221 insertions(+), 86 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 04ca94bdcb..2f4a1fb107 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -700,11 +700,20 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element; // @public (undocumented) export type MissingAnnotationEmptyStateClassKey = 'code'; -// @public (undocumented) +// @public export const MobileSidebar: ( props: React_2.PropsWithChildren<{}>, ) => JSX.Element | null; +// @public +export const MobileSidebarContext: React_2.Context; + +// @public +export type MobileSidebarContextType = { + selectedMenuItemIndex: number; + setSelectedMenuItemIndex: React_2.Dispatch>; +}; + // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -844,7 +853,7 @@ export type SelectItem = { value: string | number; }; -// @public (undocumented) +// @public export const Sidebar: ( props: React_2.PropsWithChildren, ) => JSX.Element; @@ -879,12 +888,12 @@ export const sidebarConfig: { // Warning: (ae-missing-release-tag) "SidebarContext" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export const SidebarContext: Context; // Warning: (ae-missing-release-tag) "SidebarContextType" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export type SidebarContextType = { isOpen: boolean; setOpen?: (open: boolean) => void; @@ -1167,16 +1176,14 @@ export type SidebarDividerClassKey = 'root'; // @public export const SidebarExpandButton: () => JSX.Element | null; -// @public (undocumented) +// @public export const SidebarGroup: ( props: React_2.PropsWithChildren, ) => JSX.Element; -// @public (undocumented) +// @public export interface SidebarGroupProps extends BottomNavigationActionProps { - // (undocumented) priority?: number; - // (undocumented) to?: string; } @@ -1196,19 +1203,19 @@ export type SidebarIntroClassKey = // Warning: (ae-forgotten-export) The symbol "SidebarItemProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SidebarItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // -// @public (undocumented) +// @public export const SidebarItem: React_2.ForwardRefExoticComponent< SidebarItemProps & React_2.RefAttributes >; -// Warning: (ae-missing-release-tag) "SidebarItemClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// // @public (undocumented) export type SidebarItemClassKey = | 'root' | 'buttonItem' | 'closed' | 'open' + | 'highlightable' + | 'highlighted' | 'label' | 'iconContainer' | 'searchRoot' @@ -1216,6 +1223,10 @@ export type SidebarItemClassKey = | 'searchFieldHTMLInput' | 'searchContainer' | 'secondaryAction' + | 'closedItemIcon' + | 'submenuArrow' + | 'expandButton' + | 'arrows' | 'selected'; // Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2058,10 +2069,10 @@ export const SidebarSpacer: React_2.ComponentType< // @public (undocumented) export type SidebarSpacerClassKey = 'root'; -// @public (undocumented) +// @public export const SidebarStateContext: React_2.Context; -// @public (undocumented) +// @public export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; diff --git a/packages/core-components/src/layout/Page/Page.tsx b/packages/core-components/src/layout/Page/Page.tsx index a1efe4dda9..b013affee3 100644 --- a/packages/core-components/src/layout/Page/Page.tsx +++ b/packages/core-components/src/layout/Page/Page.tsx @@ -14,9 +14,9 @@ * limitations under the License. */ +import React, { PropsWithChildren } from 'react'; import { BackstageTheme } from '@backstage/theme'; import { makeStyles, ThemeProvider } from '@material-ui/core/styles'; -import React, { PropsWithChildren } from 'react'; export type PageClassKey = 'root'; diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index acfd949ac5..0e0a7d5aa2 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -14,19 +14,23 @@ * limitations under the License. */ -import React from 'react'; import { renderInTestApp } from '@backstage/test-utils'; +import AcUnitIcon from '@material-ui/icons/AcUnit'; +import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; +import BuildRoundedIcon from '@material-ui/icons/BuildRounded'; +import MenuBookIcon from '@material-ui/icons/MenuBook'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import BuildRoundedIcon from '@material-ui/icons/BuildRounded'; -import CreateComponentIcon from '@material-ui/icons/AddCircleOutline'; -import MenuBookIcon from '@material-ui/icons/MenuBook'; -import AcUnitIcon from '@material-ui/icons/AcUnit'; -import { Sidebar } from './Bar'; -import { SidebarItem, SidebarSearchField, SidebarExpandButton } from './Items'; -import { SidebarSubmenuItem } from './SidebarSubmenuItem'; -import { SidebarSubmenu } from './SidebarSubmenu'; -import { SidebarStateContext } from '.'; +import React from 'react'; +import { + Sidebar, + SidebarExpandButton, + SidebarItem, + SidebarSearchField, + SidebarStateContext, + SidebarSubmenu, + SidebarSubmenuItem, +} from '.'; async function renderScalableSidebar() { await renderInTestApp( @@ -37,7 +41,7 @@ async function renderScalableSidebar() { toggleSidebarPinState: () => {}, }} > - + {}} to="/search" /> {}} text="Catalog"> diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 51d4268d05..c3eddfe218 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -36,7 +36,7 @@ const useStyles = makeStyles( left: 0, top: 0, bottom: 0, - zIndex: 1000, + zIndex: theme.zIndex.appBar, background: theme.palette.navigation.background, overflowX: 'hidden', msOverflowStyle: 'none', @@ -77,12 +77,23 @@ export type SidebarProps = { disableExpandOnHover?: boolean; }; -const DesktopSidebar = ({ - openDelayMs = sidebarConfig.defaultOpenDelayMs, - closeDelayMs = sidebarConfig.defaultCloseDelayMs, - disableExpandOnHover, - children, -}: PropsWithChildren) => { +/** + * Places the Sidebar & wraps the children providing context weather the `Sidebar` is open or not. + * + * Handles & delays hover events for expanding the `Sidebar` + * + * @param props `disableExpandOnHover` disables the default hover behaviour; + * `openDelayMs` & `closeDelayMs` set delay until sidebar will open/close on hover + * @returns + * @internal + */ +const DesktopSidebar = (props: PropsWithChildren) => { + const { + openDelayMs = sidebarConfig.defaultOpenDelayMs, + closeDelayMs = sidebarConfig.defaultCloseDelayMs, + disableExpandOnHover, + children, + } = props; const classes = useStyles(); const isSmallScreen = useMediaQuery( theme => theme.breakpoints.down('md'), @@ -130,6 +141,9 @@ const DesktopSidebar = ({ const isOpen = (state === State.Open && !isSmallScreen) || isPinned; + /** + * Close/Open Sidebar directily without delays. Also toggles `SidebarPinState` to avoid hidden content behind Sidebar. + */ const setOpen = (open: boolean) => { if (open) { setState(State.Open); @@ -163,7 +177,11 @@ const DesktopSidebar = ({ ); }; -/** @public */ +/** + * Passing children into the desktop or mobile sidebar depending on the context + * + * @public + */ export const Sidebar = (props: React.PropsWithChildren) => { const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; const { isMobile } = useContext(SidebarStateContext); diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 7e95ecb92b..f1b517cbce 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -37,9 +37,9 @@ import React, { import { Link, NavLinkProps, + resolvePath, useLocation, useResolvedPath, - resolvePath, } from 'react-router-dom'; import { sidebarConfig, @@ -56,11 +56,14 @@ import DoubleArrowRight from './icons/DoubleArrowRight'; import { isLocationMatch } from './utils'; import { Location } from 'history'; +/** @public */ export type SidebarItemClassKey = | 'root' | 'buttonItem' | 'closed' | 'open' + | 'highlightable' + | 'highlighted' | 'label' | 'iconContainer' | 'searchRoot' @@ -68,6 +71,10 @@ export type SidebarItemClassKey = | 'searchFieldHTMLInput' | 'searchContainer' | 'secondaryAction' + | 'closedItemIcon' + | 'submenuArrow' + | 'expandButton' + | 'arrows' | 'selected'; const useStyles = makeStyles( @@ -238,12 +245,18 @@ const useStyles = makeStyles( { name: 'BackstageSidebarItem' }, ); +/** + * Evaluates the routes of the SubmenuItems & nested DropdownItems. + * The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization. + * + * @param submenu SidebarSubmenu component + * @param location Location + * @returns boolean + */ const useLocationMatch = ( submenu: React.ReactElement, location: Location, ): boolean => - // Evaluates the routes of the SubmenuItems & nested DropdownItems. - // The reeveluation is only triggered, if the `locationPathname` changes, as `useElementFilter` uses memorization useElementFilter( submenu.props.children, elements => { @@ -367,6 +380,9 @@ export const WorkaroundNavLink = React.forwardRef< ); }); +/** + * Common component used by SidebarItem & SidebarItemWithSubmenu + */ const SidebarItemBase = forwardRef((props, ref) => { const { icon: Icon, @@ -507,6 +523,11 @@ const SidebarItemWithSubmenu = ({ ); }; +/** + * Creates a `SidebarItem` + * + * If children contain a `SidebarSubmenu` component the `SidebarItem` will have a expandable submenu + */ export const SidebarItem = forwardRef((props, ref) => { // Filter children for SidebarSubmenu components const [submenu] = useElementFilter(props.children, elements => @@ -655,6 +676,8 @@ export const SidebarScrollWrapper = styled('div')(({ theme }) => { * A button which allows you to expand the sidebar when clicked. * Use optionally to replace sidebar's expand-on-hover feature with expand-on-click. * + * If you are using this you might want to set the `disableExpandOnHover` of the `Sidebar` to `true`. + * * @public */ export const SidebarExpandButton = () => { diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx index 6f6463a3dc..8dd06b6e2b 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.test.tsx @@ -21,32 +21,39 @@ import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { Sidebar } from './Bar'; -import { SidebarItem } from './Items'; -import { MobileSidebar } from './MobileSidebar'; -import { SidebarGroup } from './SidebarGroup'; +import { + MobileSidebar, + Sidebar, + SidebarGroup, + SidebarItem, + SidebarPage, +} from '.'; const MobileSidebarWithGroups = () => ( - -

Header

- } label="Menu"> - - - - -
Content
-
More Content
- } label="Create" to="#" /> -
Footer
-
+ + +

Header

+ } label="Menu"> + + + + +
Content
+
More Content
+ } label="Create" to="#" /> +
Footer
+
+
); const MobileSidebarWithoutGroups = () => ( - - - - - + + + + + + + ); describe('', () => { @@ -56,9 +63,11 @@ describe('', () => { it('should render MobileSidebar on smaller screens', async () => { const { getByTestId } = await renderInTestApp( - - - , + + + + + , ); expect(getByTestId('mobile-sidebar-root')).toBeVisible(); }); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 158b20e6be..af66dcc322 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -31,7 +31,12 @@ import { SidebarContext } from '.'; import { sidebarConfig } from './config'; import { SidebarGroup } from './SidebarGroup'; -type MobileSidebarContextType = { +/** + * Type of `MobileSidebarContext` + * + * @public + */ +export type MobileSidebarContextType = { selectedMenuItemIndex: number; setSelectedMenuItemIndex: React.Dispatch>; }; @@ -111,12 +116,25 @@ const OverlayMenu = ({ ); }; +/** + * Context on which `SidebarGroup` is currently selected + * + * @public + */ export const MobileSidebarContext = createContext({ selectedMenuItemIndex: -1, setSelectedMenuItemIndex: () => {}, }); -/** @public */ +/** + * A navigation component for mobile screens, which sticks to the bottom. + * + * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroups` + * either rendering them as a link or an overlay menu. + * If no `SidebarGroups` are provided the sidebar content is wrapped in an default overlay menu. + * + * @public + */ export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { const { children } = props; const classes = useStyles(); @@ -160,9 +178,8 @@ export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { > setSelectedMenuItemIndex(-1)} diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 26b3426eb0..2bc4ed195b 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -47,14 +47,22 @@ const useStyles = makeStyles( { name: 'BackstageSidebarPage' }, ); -/** @public */ +/** + * Type of `SidebarStateContext` + * + * @public + */ export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; isMobile: boolean; }; -/** @public */ +/** + * Contains the state on how the `Sidebar` is rendered + * + * @public + */ export const SidebarStateContext = createContext({ isPinned: true, toggleSidebarPinState: () => {}, diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx index 664495067c..e4d7e21c50 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -20,32 +20,42 @@ import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { SidebarItem } from './Items'; -import { MobileSidebarContext } from './MobileSidebar'; -import { SidebarGroup } from './SidebarGroup'; +import { + MobileSidebarContext, + SidebarGroup, + SidebarItem, + SidebarPage, +} from '.'; const SidebarGroupWithItems = () => ( - } label="Menu"> - - - - + + } label="Menu"> + + + + + ); describe('', () => { - it('should render Items in BottomNavigationAciton on small screens', async () => { + it('should render Items in BottomNavigationAction on small screens', async () => { mockBreakpoint({ matches: true }); - const { findByRole, container } = await renderInTestApp( + const { getByRole, getAllByRole } = await renderInTestApp( , ); - expect(container.childNodes.length).toBe(1); - expect(await findByRole('button')).toBeVisible(); + expect(getAllByRole('button').length).toEqual(1); + expect(getByRole('button')).toBeVisible(); }); it('should render Items without wrapper on bigger screens', async () => { mockBreakpoint({ matches: false }); - const { container } = await renderInTestApp(); - expect(container.childNodes.length).toBe(3); + const { getByRole, queryByRole } = await renderInTestApp( + , + ); + expect(queryByRole('button')).not.toBeInTheDocument(); + expect(getByRole('link', { name: 'Home' })).toBeVisible(); + expect(getByRole('link', { name: 'Explore' })).toBeVisible(); + expect(getByRole('link', { name: 'Docs' })).toBeVisible(); }); it('should trigger update of MobileSidebarContext', async () => { diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index 1a43f703bf..f80af385c8 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -27,9 +27,20 @@ import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; -/** @public */ +/** + * Props for the `SidebarGroup` + * + * @public + */ export interface SidebarGroupProps extends BottomNavigationActionProps { + /** + * If the `SidebarGroup` should be a `Link`, `to` should be a pathname to that location + */ to?: string; + /** + * If the `SidebarGroups` should be in a different order than in the normal `Sidebar`, you can provide + * each `SidebarGroup` it's own priority to reorder them. + */ priority?: number; } @@ -51,6 +62,13 @@ const useStyles = makeStyles(theme => ({ }, })); +/** + * Returns a MUI `BottomNavigationAction`, which is aware of the current location & the selected item in the `BottomNavigation`, + * such that it will highlight a `MobileSidebarGroup` either on location change or if the selected item changes. + * + * @param props `to`: pathname of link; `value`: index of the selected item + * @internal + */ const MobileSidebarGroup = (props: SidebarGroupProps) => { const { to, label, icon, value } = props; const classes = useStyles(); @@ -88,7 +106,14 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { ); }; -/** @public */ +/** + * Groups items of the `Sidebar` together. + * + * On bigger screens, this won't have any effect at the moment. + * On small screens, it will add an action to the bottom navigation - either triggering an overlay menu or acting as a link + * + * @public + */ export const SidebarGroup = ( props: React.PropsWithChildren, ) => { diff --git a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx index 47aba5e18e..1ede5f887e 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarSubmenuItem.tsx @@ -122,7 +122,6 @@ export const SidebarSubmenuItem = (props: SidebarSubmenuItemProps) => { const { title, to, icon: Icon, dropdownItems } = props; const classes = useStyles(); const { setIsHoveredOn } = useContext(SidebarItemWithSubmenuContext); - const closeSubmenu = () => { setIsHoveredOn(false); }; diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index a26a3dbc0d..0ef5282997 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -58,11 +58,17 @@ export const submenuConfig = { export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; +/** + * Types for the `SidebarContext` + */ export type SidebarContextType = { isOpen: boolean; setOpen?: (open: boolean) => void; }; +/** + * Context wether the `Sidebar` is open + */ export const SidebarContext = createContext({ isOpen: false, }); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 4a1e1c0474..a9d31f465b 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,7 +15,8 @@ */ export { Sidebar } from './Bar'; -export { MobileSidebar } from './MobileSidebar'; +export { MobileSidebar, MobileSidebarContext } from './MobileSidebar'; +export type { MobileSidebarContextType } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarSubmenuItem } from './SidebarSubmenuItem'; diff --git a/packages/core-components/src/overridableComponents.ts b/packages/core-components/src/overridableComponents.ts index 8044da4c81..c53c47da1b 100644 --- a/packages/core-components/src/overridableComponents.ts +++ b/packages/core-components/src/overridableComponents.ts @@ -87,6 +87,8 @@ import { SidebarSpacerClassKey, SidebarDividerClassKey, SidebarIntroClassKey, + SidebarItemClassKey, + SidebarPageClassKey, CustomProviderClassKey, SignInPageClassKey, TabbedCardClassKey, @@ -162,6 +164,8 @@ type BackstageComponentsNameToClassKey = { BackstageSidebarSpacer: SidebarSpacerClassKey; BackstageSidebarDivider: SidebarDividerClassKey; BackstageSidebarIntro: SidebarIntroClassKey; + BackstageSidebarItem: SidebarItemClassKey; + BackstageSidebarPage: SidebarPageClassKey; BackstageCustomProvider: CustomProviderClassKey; BackstageSignInPage: SignInPageClassKey; BackstageTabbedCard: TabbedCardClassKey; diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 915edc94cb..946d22a9f0 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -20,7 +20,7 @@ import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const isMobile = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarStateContext); return ( From 8c833df12a6730edd927ba7120a32545615165e7 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 29 Dec 2021 18:34:18 +0100 Subject: [PATCH 040/116] Fix dublicated css from merge Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/Items.tsx | 55 ++----------------- 1 file changed, 5 insertions(+), 50 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index f1b517cbce..ed090690f0 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -185,59 +185,14 @@ const useStyles = makeStyles( borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, color: theme.palette.navigation.selectedColor, }, - buttonItem: { - background: 'none', - border: 'none', - width: 'auto', - margin: 0, - padding: 0, - textAlign: 'inherit', - font: 'inherit', - }, - closed: { + '&$closed': { width: drawerWidthClosed, - justifyContent: 'center', }, - open: { - width: drawerWidthOpen, + '& $closedItemIcon': { + paddingRight: selectedIndicatorWidth, }, - label: { - // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs - fontWeight: 'bold', - whiteSpace: 'nowrap', - lineHeight: 'auto', - flex: '3 1 auto', - width: '110px', - overflow: 'hidden', - 'text-overflow': 'ellipsis', - }, - iconContainer: { - boxSizing: 'border-box', - height: '100%', - width: iconContainerWidth, - marginRight: -theme.spacing(2), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - }, - searchRoot: { - marginBottom: 12, - }, - searchField: { - color: '#b5b5b5', - fontWeight: 'bold', - fontSize: theme.typography.fontSize, - }, - searchFieldHTMLInput: { - padding: theme.spacing(2, 0, 2), - }, - searchContainer: { - width: drawerWidthOpen - iconContainerWidth, - }, - secondaryAction: { - width: theme.spacing(6), - textAlign: 'center', - marginRight: theme.spacing(1), + '& $iconContainer': { + marginLeft: -selectedIndicatorWidth, }, }, }; From 1136ed83e3e3ba30a031e80bc0bdea11be4d0c36 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Sun, 2 Jan 2022 11:39:33 -0500 Subject: [PATCH 041/116] wip: warn users of deprecated keys in app configurations Signed-off-by: Colton Padden --- packages/config-loader/api-report.md | 1 + packages/config-loader/package.json | 1 + .../config-loader/src/lib/schema/collect.ts | 4 +- .../src/lib/schema/compile.test.ts | 33 +++++++++++ .../config-loader/src/lib/schema/compile.ts | 9 +++ .../src/lib/schema/filtering.test.ts | 27 ++++++++- .../config-loader/src/lib/schema/filtering.ts | 31 ++++++++++ .../config-loader/src/lib/schema/load.test.ts | 56 ++++++++++++++++--- packages/config-loader/src/lib/schema/load.ts | 18 +++++- .../config-loader/src/lib/schema/types.ts | 14 +++++ packages/config/api-report.md | 4 ++ packages/config/src/reader.ts | 20 ++++++- packages/config/src/types.ts | 6 ++ 13 files changed, 210 insertions(+), 14 deletions(-) diff --git a/packages/config-loader/api-report.md b/packages/config-loader/api-report.md index 269159aab4..6a02a5632f 100644 --- a/packages/config-loader/api-report.md +++ b/packages/config-loader/api-report.md @@ -21,6 +21,7 @@ export type ConfigSchemaProcessingOptions = { visibility?: ConfigVisibility[]; valueTransform?: TransformFunc; withFilteredKeys?: boolean; + withDeprecatedKeys?: boolean; }; // Warning: (ae-missing-release-tag) "ConfigTarget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index d909cde58c..2ecea8e843 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -41,6 +41,7 @@ "json-schema": "^0.4.0", "json-schema-merge-allof": "^0.8.1", "json-schema-traverse": "^1.0.0", + "lodash": "^4.17.21", "node-fetch": "^2.6.1", "typescript-json-schema": "^0.52.0", "yaml": "^1.9.2", diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index 8e7f582e44..aa3202a1b5 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -182,10 +182,10 @@ function compileTsSchemas(paths: string[]) { program, // All schemas should export a `Config` symbol 'Config', - // This enables usage of @visibility is doc comments + // This enables usage of @visibility and @deprecated is doc comments { required: true, - validationKeywords: ['visibility'], + validationKeywords: ['visibility', 'deprecated'], }, [path.split(sep).join('/')], // Unix paths are expected for all OSes here ) as JsonObject | null; diff --git a/packages/config-loader/src/lib/schema/compile.test.ts b/packages/config-loader/src/lib/schema/compile.test.ts index 1e63f3cbf8..7615fa80f5 100644 --- a/packages/config-loader/src/lib/schema/compile.test.ts +++ b/packages/config-loader/src/lib/schema/compile.test.ts @@ -40,6 +40,7 @@ describe('compileConfigSchemas', () => { ], visibilityByDataPath: new Map(), visibilityBySchemaPath: new Map(), + deprecationBySchemaPath: new Map(), }); expect(validate([{ data: { b: 'b' }, context: 'test' }])).toEqual({ errors: [ @@ -53,6 +54,7 @@ describe('compileConfigSchemas', () => { ], visibilityByDataPath: new Map(), visibilityBySchemaPath: new Map(), + deprecationBySchemaPath: new Map(), }); }); @@ -112,6 +114,7 @@ describe('compileConfigSchemas', () => { '/properties/d/items': 'frontend', }), ), + deprecationBySchemaPath: new Map(), }); }); @@ -137,4 +140,34 @@ describe('compileConfigSchemas', () => { "Config schema visibility is both 'frontend' and 'secret' for properties/a/visibility", ); }); + + it('should discover deprecations', () => { + const validate = compileConfigSchemas([ + { + path: 'a1', + value: { + type: 'object', + properties: { + a: { type: 'string', deprecated: 'deprecation reason for a' }, + b: { type: 'string', deprecated: 'deprecation reason for b' }, + c: { type: 'string' }, + }, + }, + }, + ]); + expect( + validate([ + { data: { a: 'a', b: 'b', c: 'c', d: ['d'] }, context: 'test' }, + ]), + ).toEqual({ + deprecationBySchemaPath: new Map( + Object.entries({ + '/properties/a': 'deprecation reason for a', + '/properties/b': 'deprecation reason for b', + }), + ), + visibilityByDataPath: new Map(), + visibilityBySchemaPath: new Map(), + }); + }); }); diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 1cbf5a404d..6dcd4713f5 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -81,6 +81,13 @@ export function compileConfigSchemas( const merged = mergeConfigSchemas(schemas.map(_ => _.value)); const validate = ajv.compile(merged); + const deprecationBySchemaPath = new Map(); + traverse(merged, (schema, path) => { + if ('deprecated' in schema) { + deprecationBySchemaPath.set(path, schema.deprecated); + } + }); + const visibilityBySchemaPath = new Map(); traverse(merged, (schema, path) => { if (schema.visibility && schema.visibility !== 'backend') { @@ -99,12 +106,14 @@ export function compileConfigSchemas( errors: validate.errors ?? [], visibilityByDataPath: new Map(visibilityByDataPath), visibilityBySchemaPath, + deprecationBySchemaPath, }; } return { visibilityByDataPath: new Map(visibilityByDataPath), visibilityBySchemaPath, + deprecationBySchemaPath, }; }; } diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts index be07b915ed..2324bebc9d 100644 --- a/packages/config-loader/src/lib/schema/filtering.test.ts +++ b/packages/config-loader/src/lib/schema/filtering.test.ts @@ -16,7 +16,11 @@ import { JsonObject } from '@backstage/types'; import { ConfigVisibility } from './types'; -import { filterByVisibility, filterErrorsByVisibility } from './filtering'; +import { + filterByVisibility, + filterErrorsByVisibility, + filterByDeprecated, +} from './filtering'; const data = { arr: ['f', 'b', 's'], @@ -64,6 +68,13 @@ const visibility = new Map( }), ); +const deprecations = new Map( + Object.entries({ + '/properties/arr': 'deprecated array', + '/properties/objB/properties/never': 'deprecated nested property', + }), +); + describe('filterByVisibility', () => { test.each<[ConfigVisibility[], JsonObject]>([ [ @@ -381,3 +392,17 @@ describe('filterErrorsByVisibility', () => { ]); }); }); + +describe('filterByDeprecated', () => { + it('should allow empty list of deprecations', () => { + expect(filterByDeprecated(data, new Map())).toEqual({ deprecatedKeys: [] }); + }); + it('should return a list of deprecated properties', () => { + expect(filterByDeprecated(data, deprecations)).toEqual({ + deprecatedKeys: [ + { key: 'arr', description: 'deprecated array' }, + { key: 'objB.never', description: 'deprecated nested property' }, + ], + }); + }); +}); diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index b2f39d3cc4..f955343066 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -15,6 +15,7 @@ */ import { JsonObject, JsonValue } from '@backstage/types'; +import { has } from 'lodash'; import { ConfigVisibility, DEFAULT_CONFIG_VISIBILITY, @@ -22,6 +23,36 @@ import { ValidationError, } from './types'; +/** + * This filters data by deprecation status to only include those which have been deprecated + * + * @param data - configuration data + * @param deprecationBySchemaPath - mapping of schema path to deprecation description + * @returns deprecated configuration keys with their deprecation description + */ +export function filterByDeprecated( + data: JsonObject, + deprecationBySchemaPath: Map, + withDeprecatedKeys: boolean = true, +): { deprecatedKeys: { key: string; description: string }[] } { + const deprecatedKeys = new Array<{ key: string; description: string }>(); + if (!withDeprecatedKeys) { + return { deprecatedKeys }; + } + + deprecationBySchemaPath.forEach((desc, schemaPath) => { + // convert schema path to object path (/properties/techdocs/properties/storageUrl -> techdocs.storageUrl) + const propertyPath = schemaPath + .replace(/^\/properties\//, '') // remove leading `/properties/` entirely + .replace(/\/properties\//g, '.'); // replace all other `/properties/` with a `.` + if (has(data, propertyPath)) { + deprecatedKeys.push({ key: propertyPath, description: desc }); + } + }); + + return { deprecatedKeys }; +} + /** * This filters data by visibility by discovering the visibility of each * value, and then only keeping the ones that are specified in `includeVisibilities`. diff --git a/packages/config-loader/src/lib/schema/load.test.ts b/packages/config-loader/src/lib/schema/load.test.ts index e2ae3cc2c7..a2f1df8d5f 100644 --- a/packages/config-loader/src/lib/schema/load.test.ts +++ b/packages/config-loader/src/lib/schema/load.test.ts @@ -62,7 +62,11 @@ describe('loadConfigSchema', () => { expect(schema.process(configs)).toEqual(configs); expect(schema.process(configs, { visibility: ['frontend'] })).toEqual([ - { data: { key1: 'a' }, context: 'test' }, + { + data: { key1: 'a' }, + context: 'test', + deprecatedKeys: [], + }, ]); expect( schema.process(configs, { @@ -71,7 +75,12 @@ describe('loadConfigSchema', () => { withFilteredKeys: true, }), ).toEqual([ - { data: { key1: 'X' }, context: 'test', filteredKeys: ['key2'] }, + { + data: { key1: 'X' }, + context: 'test', + filteredKeys: ['key2'], + deprecatedKeys: [], + }, ]); expect( schema.process(configs, { @@ -79,14 +88,23 @@ describe('loadConfigSchema', () => { withFilteredKeys: true, }), ).toEqual([ - { data: { key1: 'X', key2: 'X' }, context: 'test', filteredKeys: [] }, + { + data: { key1: 'X', key2: 'X' }, + context: 'test', + filteredKeys: [], + deprecatedKeys: [], + }, ]); const serialized = schema.serialize(); const schema2 = await loadConfigSchema({ serialized }); expect(schema2.process(configs, { visibility: ['frontend'] })).toEqual([ - { data: { key1: 'a' }, context: 'test' }, + { + data: { key1: 'a' }, + context: 'test', + deprecatedKeys: [], + }, ]); expect(() => schema2.process([...configs, { data: { key1: 3 }, context: 'test2' }]), @@ -131,7 +149,11 @@ describe('loadConfigSchema', () => { 'Config validation failed, Config should be number { type=number } at /key2', ); expect(schema.process(configs, { visibility: ['frontend'] })).toEqual([ - { data: { key1: 'a' }, context: 'test' }, + { + data: { key1: 'a' }, + context: 'test', + deprecatedKeys: [], + }, ]); expect(() => schema.process(configs, { visibility: ['secret'] })).toThrow( 'Config validation failed, Config should be number { type=number } at /key2', @@ -179,7 +201,13 @@ describe('loadConfigSchema', () => { ]; expect( schema.process(mkConfig({ x: 1 }), { visibility: ['frontend'] }), - ).toEqual([{ data: { nested: [{}] }, context: 'test' }]); + ).toEqual([ + { + data: { nested: [{}] }, + context: 'test', + deprecatedKeys: [], + }, + ]); expect(() => schema.process(mkConfig({ y: 1 }))).toThrow( 'Config validation failed, Config should be string { type=string } at /nested/0/y', ); @@ -190,10 +218,22 @@ describe('loadConfigSchema', () => { ); expect( schema.process(mkConfig({ x: 'a' }), { visibility: ['frontend'] }), - ).toEqual([{ data: { nested: [{}] }, context: 'test' }]); + ).toEqual([ + { + data: { nested: [{}] }, + context: 'test', + deprecatedKeys: [], + }, + ]); expect( schema.process(mkConfig({ y: 'aaa' }), { visibility: ['frontend'] }), - ).toEqual([{ data: { nested: [{ y: 'aaa' }] }, context: 'test' }]); + ).toEqual([ + { + data: { nested: [{ y: 'aaa' }] }, + context: 'test', + deprecatedKeys: [], + }, + ]); expect(() => schema.process(mkConfig({ y: 'aaaa' }), { visibility: ['frontend'] }), ).toThrow( diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 2d78785304..303fdfade9 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -18,7 +18,11 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { compileConfigSchemas } from './compile'; import { collectConfigSchemas } from './collect'; -import { filterByVisibility, filterErrorsByVisibility } from './filtering'; +import { + filterByVisibility, + filterErrorsByVisibility, + filterByDeprecated, +} from './filtering'; import { ValidationError, ConfigSchema, @@ -82,7 +86,7 @@ export async function loadConfigSchema( return { process( configs: AppConfig[], - { visibility, valueTransform, withFilteredKeys } = {}, + { visibility, valueTransform, withFilteredKeys, withDeprecatedKeys } = {}, ): AppConfig[] { const result = validate(configs); @@ -108,6 +112,11 @@ export async function loadConfigSchema( valueTransform, withFilteredKeys, ), + ...filterByDeprecated( + data, + result.deprecationBySchemaPath, + withDeprecatedKeys, + ), })); } else if (valueTransform) { processedConfigs = processedConfigs.map(({ data, context }) => ({ @@ -119,6 +128,11 @@ export async function loadConfigSchema( valueTransform, withFilteredKeys, ), + ...filterByDeprecated( + data, + result.deprecationBySchemaPath, + withDeprecatedKeys, + ), })); } diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index 53f4dec88e..76cc337f83 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -81,6 +81,13 @@ type ValidationResult = { * The path in the key uses the form `/properties//items/additionalProperties/` */ visibilityBySchemaPath: Map; + + /** + * The deprecated options that were discovered during validation. + * + * The path in the key uses the form `/properties//items/additionalProperties/` + */ + deprecationBySchemaPath: Map; }; /** @@ -124,6 +131,13 @@ export type ConfigSchemaProcessingOptions = { * Default: `false`. */ withFilteredKeys?: boolean; + + /** + * Whether or not to include the `deprecatedKeys` property in the output `AppConfig`s. + * + * Default: `true`. + */ + withDeprecatedKeys?: boolean; }; /** diff --git a/packages/config/api-report.md b/packages/config/api-report.md index efa6b85559..df9baeb4cd 100644 --- a/packages/config/api-report.md +++ b/packages/config/api-report.md @@ -13,6 +13,10 @@ export type AppConfig = { context: string; data: JsonObject_2; filteredKeys?: string[]; + deprecatedKeys?: { + key: string; + description: string; + }[]; }; // @public diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 8c0c4140a5..5c34b215ab 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -83,9 +83,27 @@ export class ConfigReader implements Config { // Merge together all configs into a single config with recursive fallback // readers, giving the first config object in the array the lowest priority. return configs.reduce( - (previousReader, { data, context, filteredKeys }) => { + (previousReader, { data, context, filteredKeys, deprecatedKeys }) => { const reader = new ConfigReader(data, context, previousReader); reader.filteredKeys = filteredKeys; + + // TODO(cmpadden) introduce `deprecatedKeys` and `notifiedDeprecatedKeys` & use logger + if (deprecatedKeys) { + for (const { key, description } of deprecatedKeys) { + if (description) { + // eslint-disable-next-line no-console + console.warn( + `The configuration key '${key}' is deprecated and may be removed soon. (reason: ${description})`, + ); + } else { + // eslint-disable-next-line no-console + console.warn( + `The configuration key '${key}' is deprecated and may be removed soon.`, + ); + } + } + } + return reader; }, undefined!, diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index a543233277..37c9595565 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -36,6 +36,12 @@ export type AppConfig = { * This can be used to warn the user if they try to read any of these keys. */ filteredKeys?: string[]; + /** + * A list of deprecated keys that were found in the configuration when it was loaded. + * + * This can be used to warn the user if they are using deprecated properties. + */ + deprecatedKeys?: { key: string; description: string }[]; }; /** From a0142b7b04881eac754a21ec2a8898ea6738d434 Mon Sep 17 00:00:00 2001 From: blam Date: Mon, 3 Jan 2022 10:44:39 +0100 Subject: [PATCH 042/116] chore: reset the adopters.md file Signed-off-by: blam --- ADOPTERS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ADOPTERS.md b/ADOPTERS.md index 95f7812636..e0e6a818c6 100644 --- a/ADOPTERS.md +++ b/ADOPTERS.md @@ -77,5 +77,5 @@ | [Mox Bank](https://www.mox.com/) | [Nick Laqua](https://github.com/nick-laqua-dragon), [Gauthier Roebroeck](https://github.com/gauthier-roebroeck-mox) | "Single pane of glass" developer portal for providing a best-in-class developer experience to our product teams and making Mox the best tech environment in Hongkong 🥰🚀 | | [Keyloop](https://www.keyloop.com/) | [Andre Wanlin](https://github.com/awanlin) | Future-motive Developer Portal to help our teams create technology to make everything about buying and owning a car better. 🚗 | | [Simply Business](https://sbtech.simplybusiness.co.uk/) | [@addersuk](https://github.com/addersuk), [@LightningStairs](https://github.com/LightningStairs), [@punitcse](https://github.com/punitcse), [@moltenice](https://github.com/moltenice) | Central developer portal to access everything a developer needs such as docs, internal service catalog, and the ability to quickly create a new service from a template. Internally developed Backstage plugins allow us to customise the experience to how we work. | -| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding | -| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. | +| [Overwolf](https://www.overwolf.com) | [@tomwolfgang](https://github.com/tomwolfgang) | Dev portal - software catalog, tech-docs, scaffolding | +| [Hotmart](https://www.hotmart.com) | [@fabioviana-hotmart](https://github.com/fabioviana-hotmart) | The main Developers Portal to centralize docs, applications and technical metrics. | From 63fa8f83715b9a998d6ee6deb8af170cc23f8089 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Tue, 4 Jan 2022 16:24:06 -0500 Subject: [PATCH 043/116] move deprecation description key mapping into filterByVisibility Signed-off-by: Colton Padden --- .../config-loader/src/lib/schema/collect.ts | 2 +- .../src/lib/schema/compile.test.ts | 12 ++-- .../config-loader/src/lib/schema/compile.ts | 70 ++++++++++++------- .../src/lib/schema/filtering.test.ts | 51 ++++++++------ .../config-loader/src/lib/schema/filtering.ts | 46 ++++-------- .../config-loader/src/lib/schema/load.test.ts | 8 --- packages/config-loader/src/lib/schema/load.ts | 10 +-- .../config-loader/src/lib/schema/types.ts | 4 +- packages/config/src/reader.ts | 19 ++--- 9 files changed, 105 insertions(+), 117 deletions(-) diff --git a/packages/config-loader/src/lib/schema/collect.ts b/packages/config-loader/src/lib/schema/collect.ts index aa3202a1b5..a05796139b 100644 --- a/packages/config-loader/src/lib/schema/collect.ts +++ b/packages/config-loader/src/lib/schema/collect.ts @@ -182,7 +182,7 @@ function compileTsSchemas(paths: string[]) { program, // All schemas should export a `Config` symbol 'Config', - // This enables usage of @visibility and @deprecated is doc comments + // This enables usage of @visibility and @deprecated in doc comments { required: true, validationKeywords: ['visibility', 'deprecated'], diff --git a/packages/config-loader/src/lib/schema/compile.test.ts b/packages/config-loader/src/lib/schema/compile.test.ts index 7615fa80f5..7b7db87c8f 100644 --- a/packages/config-loader/src/lib/schema/compile.test.ts +++ b/packages/config-loader/src/lib/schema/compile.test.ts @@ -40,7 +40,7 @@ describe('compileConfigSchemas', () => { ], visibilityByDataPath: new Map(), visibilityBySchemaPath: new Map(), - deprecationBySchemaPath: new Map(), + deprecationByDataPath: new Map(), }); expect(validate([{ data: { b: 'b' }, context: 'test' }])).toEqual({ errors: [ @@ -54,7 +54,7 @@ describe('compileConfigSchemas', () => { ], visibilityByDataPath: new Map(), visibilityBySchemaPath: new Map(), - deprecationBySchemaPath: new Map(), + deprecationByDataPath: new Map(), }); }); @@ -114,7 +114,7 @@ describe('compileConfigSchemas', () => { '/properties/d/items': 'frontend', }), ), - deprecationBySchemaPath: new Map(), + deprecationByDataPath: new Map(), }); }); @@ -160,10 +160,10 @@ describe('compileConfigSchemas', () => { { data: { a: 'a', b: 'b', c: 'c', d: ['d'] }, context: 'test' }, ]), ).toEqual({ - deprecationBySchemaPath: new Map( + deprecationByDataPath: new Map( Object.entries({ - '/properties/a': 'deprecation reason for a', - '/properties/b': 'deprecation reason for b', + '/a': 'deprecation reason for a', + '/b': 'deprecation reason for b', }), ), visibilityByDataPath: new Map(), diff --git a/packages/config-loader/src/lib/schema/compile.ts b/packages/config-loader/src/lib/schema/compile.ts index 6dcd4713f5..b7f705751e 100644 --- a/packages/config-loader/src/lib/schema/compile.ts +++ b/packages/config-loader/src/lib/schema/compile.ts @@ -40,6 +40,7 @@ export function compileConfigSchemas( // output during validation. We work around this by having this extra piece // of state that we reset before each validation. const visibilityByDataPath = new Map(); + const deprecationByDataPath = new Map(); const ajv = new Ajv({ allErrors: true, @@ -47,28 +48,48 @@ export function compileConfigSchemas( schemas: { 'https://backstage.io/schema/config-v1': true, }, - }).addKeyword({ - keyword: 'visibility', - metaSchema: { - type: 'string', - enum: CONFIG_VISIBILITIES, - }, - compile(visibility: ConfigVisibility) { - return (_data, context) => { - if (context?.dataPath === undefined) { - return false; - } - if (visibility && visibility !== 'backend') { + }) + .addKeyword({ + keyword: 'visibility', + metaSchema: { + type: 'string', + enum: CONFIG_VISIBILITIES, + }, + compile(visibility: ConfigVisibility) { + return (_data, context) => { + if (context?.dataPath === undefined) { + return false; + } + if (visibility && visibility !== 'backend') { + const normalizedPath = context.dataPath.replace( + /\['?(.*?)'?\]/g, + (_, segment) => `/${segment}`, + ); + visibilityByDataPath.set(normalizedPath, visibility); + } + return true; + }; + }, + }) + .removeKeyword('deprecated') // remove `deprecated` keyword so that we can implement our own compiler + .addKeyword({ + keyword: 'deprecated', + metaSchema: { type: 'string' }, + compile(deprecationDescription: string) { + return (_data, context) => { + if (context?.dataPath === undefined) { + return false; + } const normalizedPath = context.dataPath.replace( /\['?(.*?)'?\]/g, (_, segment) => `/${segment}`, ); - visibilityByDataPath.set(normalizedPath, visibility); - } - return true; - }; - }, - }); + // create mapping of deprecation description and data path of property + deprecationByDataPath.set(normalizedPath, deprecationDescription); + return true; + }; + }, + }); for (const schema of schemas) { try { @@ -79,14 +100,8 @@ export function compileConfigSchemas( } const merged = mergeConfigSchemas(schemas.map(_ => _.value)); - const validate = ajv.compile(merged); - const deprecationBySchemaPath = new Map(); - traverse(merged, (schema, path) => { - if ('deprecated' in schema) { - deprecationBySchemaPath.set(path, schema.deprecated); - } - }); + const validate = ajv.compile(merged); const visibilityBySchemaPath = new Map(); traverse(merged, (schema, path) => { @@ -101,19 +116,20 @@ export function compileConfigSchemas( visibilityByDataPath.clear(); const valid = validate(config); + if (!valid) { return { errors: validate.errors ?? [], visibilityByDataPath: new Map(visibilityByDataPath), visibilityBySchemaPath, - deprecationBySchemaPath, + deprecationByDataPath, }; } return { visibilityByDataPath: new Map(visibilityByDataPath), visibilityBySchemaPath, - deprecationBySchemaPath, + deprecationByDataPath, }; }; } diff --git a/packages/config-loader/src/lib/schema/filtering.test.ts b/packages/config-loader/src/lib/schema/filtering.test.ts index 2324bebc9d..df06975c9f 100644 --- a/packages/config-loader/src/lib/schema/filtering.test.ts +++ b/packages/config-loader/src/lib/schema/filtering.test.ts @@ -16,11 +16,7 @@ import { JsonObject } from '@backstage/types'; import { ConfigVisibility } from './types'; -import { - filterByVisibility, - filterErrorsByVisibility, - filterByDeprecated, -} from './filtering'; +import { filterByVisibility, filterErrorsByVisibility } from './filtering'; const data = { arr: ['f', 'b', 's'], @@ -70,8 +66,8 @@ const visibility = new Map( const deprecations = new Map( Object.entries({ - '/properties/arr': 'deprecated array', - '/properties/objB/properties/never': 'deprecated nested property', + '/arr': 'deprecated array', + '/objB/never': 'deprecated nested property', }), ); @@ -196,9 +192,34 @@ describe('filterByVisibility', () => { [['frontend', 'backend', 'secret'], { data, filteredKeys: [] }], ])('should filter correctly with %p', (filter, expected) => { expect( - filterByVisibility(data, filter, visibility, undefined, true), + filterByVisibility( + data, + filter, + visibility, + deprecations, + undefined, + true, + false, + ), ).toEqual(expected); }); + + it('should include deprecated keys regardless of visibility', () => { + expect( + filterByVisibility( + data, + [], + visibility, + deprecations, + undefined, + true, + true, + ).deprecatedKeys, + ).toEqual([ + { key: 'arr', description: 'deprecated array' }, + { key: 'objB.never', description: 'deprecated nested property' }, + ]); + }); }); describe('filterErrorsByVisibility', () => { @@ -392,17 +413,3 @@ describe('filterErrorsByVisibility', () => { ]); }); }); - -describe('filterByDeprecated', () => { - it('should allow empty list of deprecations', () => { - expect(filterByDeprecated(data, new Map())).toEqual({ deprecatedKeys: [] }); - }); - it('should return a list of deprecated properties', () => { - expect(filterByDeprecated(data, deprecations)).toEqual({ - deprecatedKeys: [ - { key: 'arr', description: 'deprecated array' }, - { key: 'objB.never', description: 'deprecated nested property' }, - ], - }); - }); -}); diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index f955343066..11ee4cb47f 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -23,36 +23,6 @@ import { ValidationError, } from './types'; -/** - * This filters data by deprecation status to only include those which have been deprecated - * - * @param data - configuration data - * @param deprecationBySchemaPath - mapping of schema path to deprecation description - * @returns deprecated configuration keys with their deprecation description - */ -export function filterByDeprecated( - data: JsonObject, - deprecationBySchemaPath: Map, - withDeprecatedKeys: boolean = true, -): { deprecatedKeys: { key: string; description: string }[] } { - const deprecatedKeys = new Array<{ key: string; description: string }>(); - if (!withDeprecatedKeys) { - return { deprecatedKeys }; - } - - deprecationBySchemaPath.forEach((desc, schemaPath) => { - // convert schema path to object path (/properties/techdocs/properties/storageUrl -> techdocs.storageUrl) - const propertyPath = schemaPath - .replace(/^\/properties\//, '') // remove leading `/properties/` entirely - .replace(/\/properties\//g, '.'); // replace all other `/properties/` with a `.` - if (has(data, propertyPath)) { - deprecatedKeys.push({ key: propertyPath, description: desc }); - } - }); - - return { deprecatedKeys }; -} - /** * This filters data by visibility by discovering the visibility of each * value, and then only keeping the ones that are specified in `includeVisibilities`. @@ -61,10 +31,17 @@ export function filterByVisibility( data: JsonObject, includeVisibilities: ConfigVisibility[], visibilityByDataPath: Map, + deprecationByDataPath: Map, transformFunc?: TransformFunc, withFilteredKeys?: boolean, -): { data: JsonObject; filteredKeys?: string[] } { + withDeprecatedKeys?: boolean, +): { + data: JsonObject; + filteredKeys?: string[]; + deprecatedKeys?: { key: string; description: string }[]; +} { const filteredKeys = new Array(); + const deprecatedKeys = new Array<{ key: string; description: string }>(); function transform( jsonVal: JsonValue, @@ -75,6 +52,12 @@ export function filterByVisibility( visibilityByDataPath.get(visibilityPath) ?? DEFAULT_CONFIG_VISIBILITY; const isVisible = includeVisibilities.includes(visibility); + // deprecated keys are added regardless of visibility indicator + const deprecation = deprecationByDataPath.get(visibilityPath); + if (deprecation) { + deprecatedKeys.push({ key: filterPath, description: deprecation }); + } + if (typeof jsonVal !== 'object') { if (isVisible) { if (transformFunc) { @@ -140,6 +123,7 @@ export function filterByVisibility( return { filteredKeys: withFilteredKeys ? filteredKeys : undefined, + deprecatedKeys: withDeprecatedKeys ? deprecatedKeys : undefined, data: (transform(data, '', '') as JsonObject) ?? {}, }; } diff --git a/packages/config-loader/src/lib/schema/load.test.ts b/packages/config-loader/src/lib/schema/load.test.ts index a2f1df8d5f..8510ccb1d8 100644 --- a/packages/config-loader/src/lib/schema/load.test.ts +++ b/packages/config-loader/src/lib/schema/load.test.ts @@ -65,7 +65,6 @@ describe('loadConfigSchema', () => { { data: { key1: 'a' }, context: 'test', - deprecatedKeys: [], }, ]); expect( @@ -79,7 +78,6 @@ describe('loadConfigSchema', () => { data: { key1: 'X' }, context: 'test', filteredKeys: ['key2'], - deprecatedKeys: [], }, ]); expect( @@ -92,7 +90,6 @@ describe('loadConfigSchema', () => { data: { key1: 'X', key2: 'X' }, context: 'test', filteredKeys: [], - deprecatedKeys: [], }, ]); @@ -103,7 +100,6 @@ describe('loadConfigSchema', () => { { data: { key1: 'a' }, context: 'test', - deprecatedKeys: [], }, ]); expect(() => @@ -152,7 +148,6 @@ describe('loadConfigSchema', () => { { data: { key1: 'a' }, context: 'test', - deprecatedKeys: [], }, ]); expect(() => schema.process(configs, { visibility: ['secret'] })).toThrow( @@ -205,7 +200,6 @@ describe('loadConfigSchema', () => { { data: { nested: [{}] }, context: 'test', - deprecatedKeys: [], }, ]); expect(() => schema.process(mkConfig({ y: 1 }))).toThrow( @@ -222,7 +216,6 @@ describe('loadConfigSchema', () => { { data: { nested: [{}] }, context: 'test', - deprecatedKeys: [], }, ]); expect( @@ -231,7 +224,6 @@ describe('loadConfigSchema', () => { { data: { nested: [{ y: 'aaa' }] }, context: 'test', - deprecatedKeys: [], }, ]); expect(() => diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 303fdfade9..9eac629ee3 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -109,12 +109,9 @@ export async function loadConfigSchema( data, visibility, result.visibilityByDataPath, + result.deprecationByDataPath, valueTransform, withFilteredKeys, - ), - ...filterByDeprecated( - data, - result.deprecationBySchemaPath, withDeprecatedKeys, ), })); @@ -125,12 +122,9 @@ export async function loadConfigSchema( data, Array.from(CONFIG_VISIBILITIES), result.visibilityByDataPath, + result.deprecationByDataPath, valueTransform, withFilteredKeys, - ), - ...filterByDeprecated( - data, - result.deprecationBySchemaPath, withDeprecatedKeys, ), })); diff --git a/packages/config-loader/src/lib/schema/types.ts b/packages/config-loader/src/lib/schema/types.ts index 76cc337f83..75993b3c78 100644 --- a/packages/config-loader/src/lib/schema/types.ts +++ b/packages/config-loader/src/lib/schema/types.ts @@ -85,9 +85,9 @@ type ValidationResult = { /** * The deprecated options that were discovered during validation. * - * The path in the key uses the form `/properties//items/additionalProperties/` + * The path in the key uses the form `////` */ - deprecationBySchemaPath: Map; + deprecationByDataPath: Map; }; /** diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 5c34b215ab..d85ea6f482 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -87,20 +87,15 @@ export class ConfigReader implements Config { const reader = new ConfigReader(data, context, previousReader); reader.filteredKeys = filteredKeys; - // TODO(cmpadden) introduce `deprecatedKeys` and `notifiedDeprecatedKeys` & use logger + // TODO(cmpadden) `withDeprecatedKeys` is defaulting to false on app start if (deprecatedKeys) { for (const { key, description } of deprecatedKeys) { - if (description) { - // eslint-disable-next-line no-console - console.warn( - `The configuration key '${key}' is deprecated and may be removed soon. (reason: ${description})`, - ); - } else { - // eslint-disable-next-line no-console - console.warn( - `The configuration key '${key}' is deprecated and may be removed soon.`, - ); - } + // eslint-disable-next-line no-console + console.warn( + `The configuration key '${key}' of ${context} is deprecated and may be removed soon. ${ + description || '' + }`, + ); } } From 6e34e2cfbfbd83aa61ec184a78374e1c4220ca96 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Tue, 4 Jan 2022 16:29:28 -0500 Subject: [PATCH 044/116] Add `backstage-cli config:check --deprecated` to list deprecated settings Signed-off-by: Colton Padden --- .changeset/long-clouds-rest.md | 12 ++++++++++++ docs/local-dev/cli-commands.md | 1 + packages/cli/src/commands/config/validate.ts | 1 + packages/cli/src/commands/index.ts | 1 + packages/cli/src/lib/config.ts | 2 ++ 5 files changed, 17 insertions(+) create mode 100644 .changeset/long-clouds-rest.md diff --git a/.changeset/long-clouds-rest.md b/.changeset/long-clouds-rest.md new file mode 100644 index 0000000000..701be91536 --- /dev/null +++ b/.changeset/long-clouds-rest.md @@ -0,0 +1,12 @@ +--- +'@backstage/cli': patch +--- + +Introduce `--deprecated` option to `config:check` to log all deprecated app configuration properties + +```sh +$ yarn backstage-cli config:check --lax --deprecated +config:check --lax --deprecated +Loaded config from app-config.yaml +The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead. +``` diff --git a/docs/local-dev/cli-commands.md b/docs/local-dev/cli-commands.md index 412e3c4598..c65fc30da6 100644 --- a/docs/local-dev/cli-commands.md +++ b/docs/local-dev/cli-commands.md @@ -556,6 +556,7 @@ Options: --package <name> Only load config schema that applies to the given package --lax Do not require environment variables to be set --frontend Only validate the frontend configuration + --deprecated List all deprecated configuration settings --config <path> Config files to load instead of app-config.yaml (default: []) -h, --help display help for command ``` diff --git a/packages/cli/src/commands/config/validate.ts b/packages/cli/src/commands/config/validate.ts index 2d3ce60366..8d528b8300 100644 --- a/packages/cli/src/commands/config/validate.ts +++ b/packages/cli/src/commands/config/validate.ts @@ -23,5 +23,6 @@ export default async (cmd: Command) => { fromPackage: cmd.package, mockEnv: cmd.lax, fullVisibility: !cmd.frontend, + withDeprecatedKeys: cmd.deprecated, }); }; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index bef5dfc285..49e700ac1e 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -193,6 +193,7 @@ export function registerCommands(program: CommanderStatic) { ) .option('--lax', 'Do not require environment variables to be set') .option('--frontend', 'Only validate the frontend configuration') + .option('--deprecated', 'Output deprecated configuration settings') .option(...configOption) .description( 'Validate that the given configuration loads and matches schema', diff --git a/packages/cli/src/lib/config.ts b/packages/cli/src/lib/config.ts index 986e409e58..c24021fd0b 100644 --- a/packages/cli/src/lib/config.ts +++ b/packages/cli/src/lib/config.ts @@ -28,6 +28,7 @@ type Options = { fromPackage?: string; mockEnv?: boolean; withFilteredKeys?: boolean; + withDeprecatedKeys?: boolean; fullVisibility?: boolean; }; @@ -82,6 +83,7 @@ export async function loadCliConfig(options: Options) { ? ['frontend', 'backend', 'secret'] : ['frontend'], withFilteredKeys: options.withFilteredKeys, + withDeprecatedKeys: options.withDeprecatedKeys, }); const frontendConfig = ConfigReader.fromConfigs(frontendAppConfigs); From f9c5841c801a1a543c61d8acc81befbb7aa3ae1c Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Tue, 4 Jan 2022 19:33:42 -0500 Subject: [PATCH 045/116] remove deleted artifacts of filterByDeprecated Signed-off-by: Colton Padden --- packages/config-loader/package.json | 1 - packages/config-loader/src/lib/schema/filtering.ts | 1 - packages/config-loader/src/lib/schema/load.ts | 6 +----- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/config-loader/package.json b/packages/config-loader/package.json index 2ecea8e843..d909cde58c 100644 --- a/packages/config-loader/package.json +++ b/packages/config-loader/package.json @@ -41,7 +41,6 @@ "json-schema": "^0.4.0", "json-schema-merge-allof": "^0.8.1", "json-schema-traverse": "^1.0.0", - "lodash": "^4.17.21", "node-fetch": "^2.6.1", "typescript-json-schema": "^0.52.0", "yaml": "^1.9.2", diff --git a/packages/config-loader/src/lib/schema/filtering.ts b/packages/config-loader/src/lib/schema/filtering.ts index 11ee4cb47f..2855979b1f 100644 --- a/packages/config-loader/src/lib/schema/filtering.ts +++ b/packages/config-loader/src/lib/schema/filtering.ts @@ -15,7 +15,6 @@ */ import { JsonObject, JsonValue } from '@backstage/types'; -import { has } from 'lodash'; import { ConfigVisibility, DEFAULT_CONFIG_VISIBILITY, diff --git a/packages/config-loader/src/lib/schema/load.ts b/packages/config-loader/src/lib/schema/load.ts index 9eac629ee3..aa7ed06305 100644 --- a/packages/config-loader/src/lib/schema/load.ts +++ b/packages/config-loader/src/lib/schema/load.ts @@ -18,11 +18,7 @@ import { AppConfig } from '@backstage/config'; import { JsonObject } from '@backstage/types'; import { compileConfigSchemas } from './compile'; import { collectConfigSchemas } from './collect'; -import { - filterByVisibility, - filterErrorsByVisibility, - filterByDeprecated, -} from './filtering'; +import { filterByVisibility, filterErrorsByVisibility } from './filtering'; import { ValidationError, ConfigSchema, From 7c28c825377a809b62f6501d3e671a76595c8f7f Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Wed, 5 Jan 2022 16:02:39 +0100 Subject: [PATCH 046/116] Improve typing by making isMobile optional & removing PropsWithChildren Improve consistency in documentation Signed-off-by: Philipp Hugenroth --- .changeset/great-bulldogs-provide.md | 2 +- .changeset/stale-brooms-fry.md | 4 +-- .../configure-app-with-plugins.md | 2 +- packages/core-components/api-report.md | 28 ++++++++++------- .../src/layout/Sidebar/Bar.tsx | 7 +++-- .../src/layout/Sidebar/MobileSidebar.tsx | 31 ++++++++++++++----- .../src/layout/Sidebar/Page.tsx | 20 +++++++----- .../src/layout/Sidebar/SidebarGroup.tsx | 10 +++--- .../src/layout/Sidebar/index.ts | 6 +++- 9 files changed, 71 insertions(+), 39 deletions(-) diff --git a/.changeset/great-bulldogs-provide.md b/.changeset/great-bulldogs-provide.md index f076dfa545..3b51ea9bdf 100644 --- a/.changeset/great-bulldogs-provide.md +++ b/.changeset/great-bulldogs-provide.md @@ -6,6 +6,6 @@ The `Bar` component will now render a `MobileSidebar` instead of the current sid --- -**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroups` as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroups` are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item. +**Add MobileSidebar:** A navigation component, which sticks to the bottom. If there is no content in the Sidebar, it won't be rendered. If there are `children ` in the `Sidebar`, but no `SidebarGroup`s as `children`, it will render all `children` into a default overlay menu, which can be displayed by clicking a menu item. If `SidebarGroup`s are provided, it will render them in the bottom navigation. Additionally, a `MobileSidebarContext`, which wraps the component, will save the selected menu item. **Add SidebarGroup:** Groups items of the `Sidebar` together. On bigger screens, this won't have any effect at the moment. On smaller screens, it will render a given icon into the `MobileSidebar`. If a route is provided, clicking the `SidebarGroup` in the `MobileSidebar` will route to the page. If no route is provided, it will add a provided icon to the `MobileSidebar` as a menu item & will render the children into an overlay menu, which will be displayed when the menu item is clicked. diff --git a/.changeset/stale-brooms-fry.md b/.changeset/stale-brooms-fry.md index c98a427e80..18c847973e 100644 --- a/.changeset/stale-brooms-fry.md +++ b/.changeset/stale-brooms-fry.md @@ -2,7 +2,7 @@ '@backstage/create-app': patch --- -You can now add `SidebarGroups` to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`. +You can now add `SidebarGroup`s to the current `Sidebar`. This will not affect how the current sidebar is displayed, but allows a customization on how the `MobileSidebar` on smaller screens will look like. A `SidebarGroup` will be displayed with the given icon in the `MobileSidebar`. A `SidebarGroup` can either link to an existing page (e.g. `/search` or `/settings`) or wrap components, which will be displayed in a full-screen overlay menu (e.g. `Menu`). @@ -46,6 +46,6 @@ Additionally, you can order the groups differently in the `MobileSidebar` than i ``` -If you decide against adding `SidebarGroups` to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`. +If you decide against adding `SidebarGroup`s to your `Sidebar` the `MobileSidebar` will contain one default menu item, which will open a full-screen overlay menu displaying all the content of the current `Sidebar`. More information on the `SidebarGroup` & the `MobileSidebar` component can be found in the changeset for the `core-components`. diff --git a/docs/getting-started/configure-app-with-plugins.md b/docs/getting-started/configure-app-with-plugins.md index 7f522437da..1bfabc335c 100644 --- a/docs/getting-started/configure-app-with-plugins.md +++ b/docs/getting-started/configure-app-with-plugins.md @@ -94,7 +94,7 @@ import InternalToolIcon from './internal-tool.icon.svg'; On mobile devices the `Sidebar` is displayed at the bottom of the screen. For customizing the experience you can group `SidebarItems` in a `SidebarGroup` (Example 1) or create a `SidebarGroup` with a link (Example 2). All -`SidebarGroups` are displayed in the bottom navigation with an icon. +`SidebarGroup`s are displayed in the bottom navigation with an icon. ```ts // Example 1 diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index 2f4a1fb107..c2244e686d 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -701,9 +701,7 @@ export function MissingAnnotationEmptyState(props: Props_3): JSX.Element; export type MissingAnnotationEmptyStateClassKey = 'code'; // @public -export const MobileSidebar: ( - props: React_2.PropsWithChildren<{}>, -) => JSX.Element | null; +export const MobileSidebar: (props: MobileSidebarProps) => JSX.Element | null; // @public export const MobileSidebarContext: React_2.Context; @@ -714,6 +712,11 @@ export type MobileSidebarContextType = { setSelectedMenuItemIndex: React_2.Dispatch>; }; +// @public +export type MobileSidebarProps = { + children?: React_2.ReactNode; +}; + // Warning: (ae-missing-release-tag) "OAuthRequestDialog" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) @@ -854,9 +857,7 @@ export type SelectItem = { }; // @public -export const Sidebar: ( - props: React_2.PropsWithChildren, -) => JSX.Element; +export const Sidebar: (props: SidebarProps) => JSX.Element; // Warning: (ae-missing-release-tag) "SIDEBAR_INTRO_LOCAL_STORAGE" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -1177,12 +1178,11 @@ export type SidebarDividerClassKey = 'root'; export const SidebarExpandButton: () => JSX.Element | null; // @public -export const SidebarGroup: ( - props: React_2.PropsWithChildren, -) => JSX.Element; +export const SidebarGroup: (props: SidebarGroupProps) => JSX.Element; // @public export interface SidebarGroupProps extends BottomNavigationActionProps { + children?: React_2.ReactNode; priority?: number; to?: string; } @@ -1232,18 +1232,24 @@ export type SidebarItemClassKey = // Warning: (ae-missing-release-tag) "SidebarPage" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export function SidebarPage(props: PropsWithChildren<{}>): JSX.Element; +export function SidebarPage(props: SidebarPageProps): JSX.Element; // Warning: (ae-missing-release-tag) "SidebarPageClassKey" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) export type SidebarPageClassKey = 'root'; +// @public +export type SidebarPageProps = { + children?: React_2.ReactNode; +}; + // @public (undocumented) export type SidebarProps = { openDelayMs?: number; closeDelayMs?: number; disableExpandOnHover?: boolean; + children?: React_2.ReactNode; }; // Warning: (ae-missing-release-tag) "SidebarScrollWrapper" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -2076,7 +2082,7 @@ export const SidebarStateContext: React_2.Context; export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; - isMobile: boolean; + isMobile?: boolean; }; // @public diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index c3eddfe218..fe3964b745 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -17,7 +17,7 @@ import { makeStyles } from '@material-ui/core/styles'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import classnames from 'classnames'; -import React, { useState, useContext, PropsWithChildren, useRef } from 'react'; +import React, { useState, useContext, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarStateContext } from './Page'; @@ -75,6 +75,7 @@ export type SidebarProps = { openDelayMs?: number; closeDelayMs?: number; disableExpandOnHover?: boolean; + children?: React.ReactNode; }; /** @@ -87,7 +88,7 @@ export type SidebarProps = { * @returns * @internal */ -const DesktopSidebar = (props: PropsWithChildren) => { +const DesktopSidebar = (props: SidebarProps) => { const { openDelayMs = sidebarConfig.defaultOpenDelayMs, closeDelayMs = sidebarConfig.defaultCloseDelayMs, @@ -182,7 +183,7 @@ const DesktopSidebar = (props: PropsWithChildren) => { * * @public */ -export const Sidebar = (props: React.PropsWithChildren) => { +export const Sidebar = (props: SidebarProps) => { const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; const { isMobile } = useContext(SidebarStateContext); diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index af66dcc322..0761199c76 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -41,6 +41,25 @@ export type MobileSidebarContextType = { setSelectedMenuItemIndex: React.Dispatch>; }; +/** + * Props of MobileSidebar + * + * @public + */ +export type MobileSidebarProps = { + children?: React.ReactNode; +}; + +/** + * @internal + */ +type OverlayMenuProps = { + label?: string; + onClose: () => void; + open: boolean; + children?: React.ReactNode; +}; + const useStyles = makeStyles(theme => ({ root: { position: 'fixed', @@ -88,11 +107,7 @@ const OverlayMenu = ({ label = 'Menu', open, onClose, -}: React.PropsWithChildren<{ - label?: string; - onClose: () => void; - open: boolean; -}>) => { +}: OverlayMenuProps) => { const classes = useStyles(); return ( @@ -129,13 +144,13 @@ export const MobileSidebarContext = createContext({ /** * A navigation component for mobile screens, which sticks to the bottom. * - * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroups` + * It alternates the normal sidebar by grouping the `SidebarItems` based on provided `SidebarGroup`s * either rendering them as a link or an overlay menu. - * If no `SidebarGroups` are provided the sidebar content is wrapped in an default overlay menu. + * If no `SidebarGroup`s are provided the sidebar content is wrapped in an default overlay menu. * * @public */ -export const MobileSidebar = (props: React.PropsWithChildren<{}>) => { +export const MobileSidebar = (props: MobileSidebarProps) => { const { children } = props; const classes = useStyles(); const location = useLocation(); diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 2bc4ed195b..9e5d897de6 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -15,12 +15,7 @@ */ import { makeStyles } from '@material-ui/core/styles'; -import React, { - createContext, - PropsWithChildren, - useEffect, - useState, -} from 'react'; +import React, { createContext, useEffect, useState } from 'react'; import { sidebarConfig } from './config'; import { BackstageTheme } from '@backstage/theme'; import { LocalStorage } from './localStorage'; @@ -55,7 +50,16 @@ const useStyles = makeStyles( export type SidebarStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; - isMobile: boolean; + isMobile?: boolean; +}; + +/** + * Props for SidebarPage + * + * @public + */ +export type SidebarPageProps = { + children?: React.ReactNode; }; /** @@ -69,7 +73,7 @@ export const SidebarStateContext = createContext({ isMobile: false, }); -export function SidebarPage(props: PropsWithChildren<{}>) { +export function SidebarPage(props: SidebarPageProps) { const [isPinned, setIsPinned] = useState(() => LocalStorage.getSidebarPinState(), ); diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index f80af385c8..fc451cd779 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -38,10 +38,14 @@ export interface SidebarGroupProps extends BottomNavigationActionProps { */ to?: string; /** - * If the `SidebarGroups` should be in a different order than in the normal `Sidebar`, you can provide + * If the `SidebarGroup`s should be in a different order than in the normal `Sidebar`, you can provide * each `SidebarGroup` it's own priority to reorder them. */ priority?: number; + /** + * React children + */ + children?: React.ReactNode; } const useStyles = makeStyles(theme => ({ @@ -114,9 +118,7 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { * * @public */ -export const SidebarGroup = ( - props: React.PropsWithChildren, -) => { +export const SidebarGroup = (props: SidebarGroupProps) => { const { children, to, label, icon, value } = props; const { isMobile } = useContext(SidebarStateContext); diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index a9d31f465b..61fbcd25ab 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -16,7 +16,10 @@ export { Sidebar } from './Bar'; export { MobileSidebar, MobileSidebarContext } from './MobileSidebar'; -export type { MobileSidebarContextType } from './MobileSidebar'; +export type { + MobileSidebarContextType, + MobileSidebarProps, +} from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarSubmenuItem } from './SidebarSubmenuItem'; @@ -34,6 +37,7 @@ export { export type { SidebarStateContextType as SidebarStateContextType, SidebarPageClassKey, + SidebarPageProps, } from './Page'; export { SidebarDivider, From a505347140e3091915c2a7dfbf4dcb202c835734 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Wed, 5 Jan 2022 14:17:11 -0500 Subject: [PATCH 047/116] enable withDeprecatedKeys in schema process for backend-common and app-backend Signed-off-by: Colton Padden --- packages/backend-common/src/config.ts | 5 ++++- packages/config/src/reader.ts | 1 - plugins/app-backend/src/lib/config.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/backend-common/src/config.ts b/packages/backend-common/src/config.ts index 399898cfc4..8e0c05b86e 100644 --- a/packages/backend-common/src/config.ts +++ b/packages/backend-common/src/config.ts @@ -38,7 +38,10 @@ const updateRedactionList = ( configs: AppConfig[], logger: Logger, ) => { - const secretAppConfigs = schema.process(configs, { visibility: ['secret'] }); + const secretAppConfigs = schema.process(configs, { + visibility: ['secret'], + withDeprecatedKeys: true, + }); const secretConfig = ConfigReader.fromConfigs(secretAppConfigs); const values = new Set(); const data = secretConfig.get(); diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index d85ea6f482..b4bbf0722e 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -87,7 +87,6 @@ export class ConfigReader implements Config { const reader = new ConfigReader(data, context, previousReader); reader.filteredKeys = filteredKeys; - // TODO(cmpadden) `withDeprecatedKeys` is defaulting to false on app start if (deprecatedKeys) { for (const { key, description } of deprecatedKeys) { // eslint-disable-next-line no-console diff --git a/plugins/app-backend/src/lib/config.ts b/plugins/app-backend/src/lib/config.ts index c37dc34dcc..6959253336 100644 --- a/plugins/app-backend/src/lib/config.ts +++ b/plugins/app-backend/src/lib/config.ts @@ -91,7 +91,7 @@ export async function readConfigs(options: ReadOptions): Promise { const frontendConfigs = await schema.process( [{ data: config.get() as JsonObject, context: 'app' }], - { visibility: ['frontend'] }, + { visibility: ['frontend'], withDeprecatedKeys: true }, ); appConfigs.push(...frontendConfigs); } catch (error) { From f685e1398f857b129f5ff3402883ba31e12f3be1 Mon Sep 17 00:00:00 2001 From: Colton Padden Date: Wed, 5 Jan 2022 14:46:39 -0500 Subject: [PATCH 048/116] add changeset for deprecation warning feature Signed-off-by: Colton Padden --- .changeset/light-trainers-allow.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .changeset/light-trainers-allow.md diff --git a/.changeset/light-trainers-allow.md b/.changeset/light-trainers-allow.md new file mode 100644 index 0000000000..0ea13cd19e --- /dev/null +++ b/.changeset/light-trainers-allow.md @@ -0,0 +1,22 @@ +--- +'@backstage/backend-common': patch +'@backstage/config': patch +'@backstage/config-loader': patch +'@backstage/plugin-app-backend': patch +--- + +Loading of app configurations now reference the `@deprecated` construct from +JSDoc to determine if a property in-use has been deprecated. Users are notified +of deprecated keys in the format: + +```txt +The configuration key 'catalog.processors.githubOrg' of app-config.yaml is deprecated and may be removed soon. Configure a GitHub integration instead. +``` + +When the `withDeprecatedKeys` option is set to `true` in the `process` method +of `loadConfigSchema`, the user will be notified that deprecated keys have been +identified in their app configuration. + +The `backend-common` and `plugin-app-backend` packages have been updated to set +`withDeprecatedKeys` to true so that users are notified of deprecated settings +by default. From 4b57df1d86d16230f3482364b3213ca075545bf3 Mon Sep 17 00:00:00 2001 From: Julio Arias Date: Thu, 6 Jan 2022 14:55:33 -0600 Subject: [PATCH 049/116] bugfix: Fix helm chart ingress template This changes fixes the ingress spec to match the API version either v1 or v1beta1, previously the apiVersion was change according to the K8S version but no further changes where made to actually match the new v1 API spec. This cause issues deploying to clusters with versions 1.19+ Signed-off-by: Julio Arias --- contrib/chart/backstage/Chart.yaml | 2 +- .../chart/backstage/templates/ingress.yaml | 71 ++++++++++++++++--- 2 files changed, 63 insertions(+), 10 deletions(-) diff --git a/contrib/chart/backstage/Chart.yaml b/contrib/chart/backstage/Chart.yaml index 9154368475..21f7084415 100644 --- a/contrib/chart/backstage/Chart.yaml +++ b/contrib/chart/backstage/Chart.yaml @@ -5,7 +5,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. -version: 0.1.2 +version: 0.1.3 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. diff --git a/contrib/chart/backstage/templates/ingress.yaml b/contrib/chart/backstage/templates/ingress.yaml index 3ec4990561..3fa052485e 100644 --- a/contrib/chart/backstage/templates/ingress.yaml +++ b/contrib/chart/backstage/templates/ingress.yaml @@ -3,10 +3,10 @@ {{- $lighthouseUrl := urlParse .Values.appConfig.lighthouse.baseUrl}} {{/* Determine the api type for the ingress */}} -{{- if lt .Capabilities.KubeVersion.Minor "19" }} -apiVersion: networking.k8s.io/v1beta1 -{{- else if ge .Capabilities.KubeVersion.Minor "19" }} +{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} apiVersion: networking.k8s.io/v1 +{{- else -}} +apiVersion: networking.k8s.io/v1beta1 {{- end }} kind: Ingress metadata: @@ -29,31 +29,60 @@ spec: - {{ $frontendUrl.host }} - {{ $backendUrl.host }} - {{ $lighthouseUrl.host }} - rules: - host: {{ $frontendUrl.host }} http: paths: - path: / + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + pathType: Prefix + {{- end }} backend: + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + service: + name: {{ include "frontend.serviceName" . }} + port: + number: 80 + {{- else -}} serviceName: {{ include "frontend.serviceName" . }} servicePort: 80 - {{/* Route the backend inside the same hostname as the frontend when they are the same */}} - {{- if eq $frontendUrl.host $backendUrl.host}} + {{- end }} + {{/* Route the backend inside the same hostname as the frontend when they are the same */}} + {{- if eq $frontendUrl.host $backendUrl.host}} - path: /api/ + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + pathType: Prefix + {{- end }} backend: + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + service: + name: {{ include "backend.serviceName" . }} + port: + number: 80 + {{- else -}} serviceName: {{ include "backend.serviceName" . }} servicePort: 80 - {{/* Route the backend through a different host */}} - {{- else -}} + {{- end }} + {{/* Route the backend through a different host */}} + {{- else -}} - host: {{ $backendUrl.host }} http: paths: - path: {{ $backendUrl.path | default "/" }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + pathType: Prefix + {{- end }} backend: + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + service: + name: {{ include "frontend.serviceName" . }} + port: + number: 80 + {{- else -}} serviceName: {{ include "backend.serviceName" . }} servicePort: 80 - {{- end }} + {{- end }} + {{- end }} {{/* Route lighthouse through a different host */}} {{- if not ( eq $frontendUrl.host $lighthouseUrl.host ) }} @@ -61,13 +90,27 @@ spec: http: paths: - path: {{ $lighthouseUrl.path | default "/" }} + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + pathType: Prefix + {{- end }} backend: + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + service: + name: {{ include "lighthouse.serviceName" . }} + port: + number: 80 + {{- else -}} serviceName: {{ include "lighthouse.serviceName" . }} servicePort: 80 + {{- end }} {{- else }} {{/* Route lighthouse by path with re-write rules when it is hosted under the same hostname */}} --- +{{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} +apiVersion: networking.k8s.io/v1 +{{- else -}} apiVersion: networking.k8s.io/v1beta1 +{{- end }} kind: Ingress metadata: name: {{ include "backstage.fullname" . }}-ingress-lighthouse @@ -92,7 +135,17 @@ spec: http: paths: - path: {{$lighthouseUrl.path}}(/|$)(.*) + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + pathType: Prefix + {{- end }} backend: + {{- if .Capabilities.APIVersions.Has "networking.k8s.io/v1" }} + service: + name: {{ include "lighthouse.serviceName" . }} + port: + number: 80 + {{- else -}} serviceName: {{ include "lighthouse.serviceName" . }} servicePort: 80 + {{- end }} {{- end }} From 29710c91c29d5a324c81eb695c0b3e1b53ac38ba Mon Sep 17 00:00:00 2001 From: Erik Larsson Date: Sun, 9 Jan 2022 00:02:09 +0100 Subject: [PATCH 050/116] use lighter color for block quotes and horizontal rulers Signed-off-by: Erik Larsson --- .changeset/red-meals-turn.md | 5 +++++ plugins/techdocs/src/reader/components/Reader.tsx | 9 +++++++++ 2 files changed, 14 insertions(+) create mode 100644 .changeset/red-meals-turn.md diff --git a/.changeset/red-meals-turn.md b/.changeset/red-meals-turn.md new file mode 100644 index 0000000000..9d52b61642 --- /dev/null +++ b/.changeset/red-meals-turn.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-techdocs': patch +--- + +use lighter color for block quotes and horizontal rulers diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 90a3aa3489..2032853852 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -205,6 +205,13 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => { .md-typeset h1, .md-typeset h2, .md-typeset h3 { font-weight: bold; } .md-nav { font-size: 1rem; } .md-grid { max-width: 90vw; margin: 0 } + .md-typeset blockquote { + color: ${theme.palette.textSubtle}; + border-left: 0.2rem solid ${theme.palette.textVerySubtle}; + } + .md-typeset hr { + border-bottom: 0.05rem dotted ${theme.palette.textVerySubtle}; + } .md-typeset table:not([class]) { font-size: 1rem; border: 1px solid ${theme.palette.text.primary}; @@ -325,6 +332,8 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => { theme.palette.primary.main, theme.palette.success.main, theme.palette.text.primary, + theme.palette.textSubtle, + theme.palette.textVerySubtle, theme.typography.fontFamily, ], ); From fb33739fbe3984f0cc00f2c90528dc28649f9d33 Mon Sep 17 00:00:00 2001 From: Nigel Wright Date: Mon, 10 Jan 2022 08:16:21 +1300 Subject: [PATCH 051/116] updated integration instructions Signed-off-by: Nigel Wright --- docs/integrations/aws-s3/discovery.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md index 656cb9f7b3..5895ab453e 100644 --- a/docs/integrations/aws-s3/discovery.md +++ b/docs/integrations/aws-s3/discovery.md @@ -27,9 +27,9 @@ catalog: Note the `s3-discovery` type, as this is not a regular `url` processor. -As this processor is not one of the default providers, you will also need to add the below: +As this processor is not one of the default providers, you will also need to add the below to `packages/backend/src/plugins/catalog.ts`: -```javascript +```ts /* packages/backend/src/plugins/catalog.ts */ import { AwsS3DiscoveryProcessor} from '@backstage/plugin-catalog-backend'; From 4ae04a2c2fd9964344b48290100ce185a7580791 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Mon, 10 Jan 2022 10:32:56 +0100 Subject: [PATCH 052/116] Fix naming of SidebarStateContext import Signed-off-by: Philipp Hugenroth --- plugins/techdocs/src/reader/components/Reader.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 57d2786af5..424697f59d 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -31,7 +31,7 @@ import { EntityName } from '@backstage/catalog-model'; import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarPinStateContext } from '@backstage/core-components'; +import { SidebarStateContext } from '@backstage/core-components'; import { techdocsStorageApiRef } from '../../api'; @@ -146,7 +146,7 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => { const [dom, setDom] = useState(null); // sidebar pinned status to be used in computing CSS style injections - const { isPinned } = useContext(SidebarPinStateContext); + const { isPinned } = useContext(SidebarStateContext); const updateSidebarPosition = useCallback(() => { if (!dom || !sidebars) return; From 5de4482b44dc22b70b647694a44dfe63d37014e7 Mon Sep 17 00:00:00 2001 From: Nigel Wright Date: Tue, 11 Jan 2022 09:16:25 +1300 Subject: [PATCH 053/116] chore: cleaned up markdown with prettier Signed-off-by: Nigel Wright --- docs/integrations/aws-s3/discovery.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/integrations/aws-s3/discovery.md b/docs/integrations/aws-s3/discovery.md index 5895ab453e..545a578e63 100644 --- a/docs/integrations/aws-s3/discovery.md +++ b/docs/integrations/aws-s3/discovery.md @@ -27,15 +27,15 @@ catalog: Note the `s3-discovery` type, as this is not a regular `url` processor. -As this processor is not one of the default providers, you will also need to add the below to `packages/backend/src/plugins/catalog.ts`: +As this processor is not one of the default providers, you will also need to add +the below to `packages/backend/src/plugins/catalog.ts`: ```ts /* packages/backend/src/plugins/catalog.ts */ - -import { AwsS3DiscoveryProcessor} from '@backstage/plugin-catalog-backend'; +import { AwsS3DiscoveryProcessor } from '@backstage/plugin-catalog-backend'; const builder = await CatalogBuilder.create(env); /** ... other processors ... */ -builder.addProcessor(new AwsS3DiscoveryProcessor(env.reader)) +builder.addProcessor(new AwsS3DiscoveryProcessor(env.reader)); ``` From 0a80732c959c842a75dec563489c14b6fbc84fff Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Tue, 11 Jan 2022 12:45:03 +0100 Subject: [PATCH 054/116] Updated roadmap page in the public documentation Signed-off-by: Francesco Corti --- docs/overview/roadmap.md | 120 +++++++++++---------------------------- 1 file changed, 34 insertions(+), 86 deletions(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 4fde1662eb..7cd565c25e 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -7,10 +7,7 @@ description: Roadmap of Backstage ## The Backstage Roadmap Backstage is currently under rapid development. This page details the project’s -public roadmap, the result of ongoing collaboration between the core maintainers -and the broader Backstage community. Treat the roadmap as an ever-evolving guide -to keep us aligned as a community on: - +public roadmap, the result of ongoing collaboration between the core maintainers and the broader Backstage community. Treat the roadmap as an ever-evolving guide to keep us aligned as a community on: - Upcoming enhancements and benefits, - Planning contributions and support, - Planning the project’s adoption, @@ -28,20 +25,15 @@ already working) on a new or existing feature, please let us know, so that we can update the roadmap accordingly. We are also happy to share knowledge and context that will help your feature land successfully. -You can also head over to the -[CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) -guidelines to get started. +You can also head over to the [CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guidelines to get started. -If you have specific questions about the roadmap, please create an -[issue](https://github.com/backstage/backstage/issues/new/choose), ping us on -[Discord](https://discord.gg/awD6SxgQ), or -[book time](http://calendly.com/spotify-backstage) with the Spotify team. +If you have specific questions about the roadmap, please create an [issue](https://github.com/backstage/backstage/issues/new/choose), ping us on [Discord](https://discord.gg/awD6SxgQ), or [book time](http://calendly.com/spotify-backstage) with the Spotify team. ### How to read the roadmap The Backstage roadmap lays out both [“what’s next”](#whats-next) and [“future work”](#future-work). With "next" we mean features planned for release -within the ongoing quarter starting in July until September 2021 included. With +within the ongoing quarter starting in January until March 2022 included. With "future" we mean features in the radar, but not yet scheduled. The long-term roadmap (12 - 36 months) is not detailed in the public roadmap. @@ -61,59 +53,19 @@ The feature set below is planned for the ongoing quarter, and grouped by theme. The list order doesn’t necessarily reflect priority, and the development/release cycle will vary based on maintainer schedules. -### Backstage Core +### Backstage 1.0 (and following versions) -The following features are planned for release: +During the first quarter of 2022 it is planned to be finalised and released the version 1.0 of the Backstage platform (defined by the Core, [Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), [Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) and [TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview)). Included as part of this milestone: +- The cadence of minor/weekly/daily releases to provide clarity on the frequency and expectations for future versions of the platform and its defining modules. +- The support model to set the expectations from the adopters in their respective use cases. -- **Improved responsiveness:** Check out the - [RFC here](https://github.com/backstage/backstage/issues/6318) for further - details on how to improve the responsiveness for Backstage's UI. +### Backstage Security Audit -### Software Templates +This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third party companies in auditing the platform and highlighting potential vulnerabilities (if there are any). The benefit for the adopters is clear: we want Backstage to be as much secure as possible and we want to make it reliable through a specific initiative. This initiative in particular is done together (and with the support of) the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). -The following features are planned for release:: +### Moving to Incubation in CNCF -- **Re-creation/resubmission in case of failure:** Speed up productivity by - allowing developers to relaunch a project after a failure or any unexpected - problem. In the current version, this task requires retyping and a full - re-creation from scratch. -- **Performance and usability improvements for contributors:** Reach a relevant - improvement in templating's performance through the replacement of - [handlebars](https://handlebarsjs.com/). Other replacements will be considered - as part of this task (possibly - [cookiecutter](https://cookiecutter.readthedocs.io/)) for easier software - template creation, allowing more contributors to reach their goals without - having to learn new tooling. -- **Improved extensibility through inclusion:** Make software templates more - maintainable and extensible by adding `$include` support for parameters. -- **Authenticated job creation:** Created jobs will be able to run with an - authenticated user with all actions tracked for future consumption and - evidence. Track users creating jobs and make “jobs created by me” reporting - available. - -### Software Catalog - -The following features are planned for release: - -- **Request For Comments (RFC) for composability improvements (routing):** - Enable plugins to be auto-added and make plugin installation and upgrades - easier for all Backstage users. This includes information card layouts, entity - pages containing content and hooking the external header, considering the - support of a separate deployment, and configuration for plugins. -- **Removing duplicated entities in catalog:** As any adopter knows, a software - catalog can contain thousands or more entities and it is very important to - avoid duplications in naming to prevent failures. With this development task, - two entities with the same name won't be allowed as described - [here](https://github.com/backstage/backstage/issues/4760). -- **Connecting identity to ownership to prepare for role-based access control - ([RBAC](https://en.wikipedia.org/wiki/Role-based_access_control)):** This is a - first step to supporting RBAC for the software catalog (see the - [future work section](#future-work) for further details). Provide each entity - within the software catalog with a recognized owner. -- **Catalog performance improvements through improved caching:** Fix the - performance gaps in the catalog processor, which currently doesn’t have a - strong caching mechanism. The current version often requires fetching a - relevant amount of data, especially at scale. +The progress of the request can be seen [here](https://github.com/cncf/toc/pull/717). ## Future work @@ -121,32 +73,12 @@ The following feature list doesn’t represent a commitment to develop and the list order doesn’t reflect any priority or importance. But these features are on the maintainers’ radar, with clear interest expressed by the community. -- **Improved UX design:** Provide a better Backstage user experience through - visual guidelines and templates, especially navigation across plug-ins and - portal functionalities. -- **Catalog composability (routing):** Follow up development after the RFC - planned for the ongoing quarter (see [what’s next](#whats-next) for further - details). -- **Catalog-import improvements:** Provide a faster (scalability) and better - (more features like move/rename) way to import entities into the Software - Catalog. Importing items in the Software Catalog is crucial for creating a - Backstage proof-of-concept or testing/planning for broader organizational - adoption. This enhancement better supports getting developers to use Backstage - with less effort and customization. -- **Catalog improvements:** Add pagination and sourcing to Software Catalog. -- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query - Backstage backend services with a standard query language for APIs. -- **Software templates performance improvements through decoupling a separate - worker:** Improve performance through decoupling resource-consuming services - and making them asynchronous. In the current version, project auto-creation - through the Software Templating system can consume a lot of resources and - bottleneck many concurrent projects created simultaneously. -- **API discovery and documentation:** Add better support for the - [gRPC](https://grpc.io/). -- **TechDocs GA release:** Work toward enhancements necessary to get TechDocs to - general availability. Check out the - [milestone here](https://github.com/backstage/backstage/milestone/30) for - further details. +- **Backend Services:** To better scale and maintain the Backstage instances, a backend layer of services is planned to be introduced as part of the software architecture. This layer of backend services will help in decoupling the various modules (Catalog and Scaffolder just to quote some) from the front-end experience. +- **Security Plan (and Strategy):** The purpose of the Security Strategy is to move another step ahead along the path of the maturity of the platform, setting the expectations of any adopters from a security point. +- **Search GA:**. +- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query Backstage backend services with a standard query language for APIs. +- **Telemetry:** To efficiently collect, store and analyse system and application log data so that Backstage can be monitored and improved. +- **Improved UX design:** Provide a better Backstage user experience through visual guidelines and templates, especially navigation across plug-ins and portal functionalities. ## Completed milestones @@ -177,3 +109,19 @@ Read more about the completed (and released) features for reference. - Support auth providers: Google, Okta, GitHub, GitLab, [auth0](https://github.com/backstage/backstage/pull/1611), [AWS](https://github.com/backstage/backstage/pull/1990) + +- [Donate Backstage to the CNCF 🎉](https://backstage.io/blog/2020/09/23/backstage-cncf-sandbox) +- [TechDocs v1](https://backstage.io/blog/2020/09/08/announcing-tech-docs) +- [Plugin marketplace](https://backstage.io/plugins) +- [Improved and move documentation to backstage.io](https://backstage.io/docs/overview/what-is-backstage) +- [Backstage Software Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha) +- [Backstage Software Templates (alpha)](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates) +- [Make it possible to add custom auth providers](https://backstage.io/blog/2020/07/01/how-to-enable-authentication-in-backstage-using-passport) +- [TechDocs v0](https://github.com/backstage/backstage/milestone/15) +- CI plugins: CircleCI, Jenkins, GitHub Actions and TravisCI +- [Service API documentation](https://github.com/backstage/backstage/pull/1737) +- Backstage Software Catalog can read from: GitHub, GitLab, + [Bitbucket](https://github.com/backstage/backstage/pull/1938) +- Support auth providers: Google, Okta, GitHub, GitLab, + [auth0](https://github.com/backstage/backstage/pull/1611), + [AWS](https://github.com/backstage/backstage/pull/1990) From 2a8d10dc9024a6353f6558f505821ed54b4fe1bb Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 11 Jan 2022 13:32:19 +0100 Subject: [PATCH 055/116] chore: fixing formatting for the roadmap Signed-off-by: blam --- docs/overview/roadmap.md | 59 +++++++++++++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 13 deletions(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 7cd565c25e..d95db0aaa7 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -7,7 +7,10 @@ description: Roadmap of Backstage ## The Backstage Roadmap Backstage is currently under rapid development. This page details the project’s -public roadmap, the result of ongoing collaboration between the core maintainers and the broader Backstage community. Treat the roadmap as an ever-evolving guide to keep us aligned as a community on: +public roadmap, the result of ongoing collaboration between the core maintainers +and the broader Backstage community. Treat the roadmap as an ever-evolving guide +to keep us aligned as a community on: + - Upcoming enhancements and benefits, - Planning contributions and support, - Planning the project’s adoption, @@ -25,9 +28,14 @@ already working) on a new or existing feature, please let us know, so that we can update the roadmap accordingly. We are also happy to share knowledge and context that will help your feature land successfully. -You can also head over to the [CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) guidelines to get started. +You can also head over to the +[CONTRIBUTING](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md) +guidelines to get started. -If you have specific questions about the roadmap, please create an [issue](https://github.com/backstage/backstage/issues/new/choose), ping us on [Discord](https://discord.gg/awD6SxgQ), or [book time](http://calendly.com/spotify-backstage) with the Spotify team. +If you have specific questions about the roadmap, please create an +[issue](https://github.com/backstage/backstage/issues/new/choose), ping us on +[Discord](https://discord.gg/awD6SxgQ), or +[book time](http://calendly.com/spotify-backstage) with the Spotify team. ### How to read the roadmap @@ -55,17 +63,32 @@ cycle will vary based on maintainer schedules. ### Backstage 1.0 (and following versions) -During the first quarter of 2022 it is planned to be finalised and released the version 1.0 of the Backstage platform (defined by the Core, [Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), [Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) and [TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview)). Included as part of this milestone: -- The cadence of minor/weekly/daily releases to provide clarity on the frequency and expectations for future versions of the platform and its defining modules. -- The support model to set the expectations from the adopters in their respective use cases. +During the first quarter of 2022 it is planned to be finalised and released the +version 1.0 of the Backstage platform (defined by the Core, +[Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), +[Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) +and [TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview)). +Included as part of this milestone: + +- The cadence of minor/weekly/daily releases to provide clarity on the frequency + and expectations for future versions of the platform and its defining modules. +- The support model to set the expectations from the adopters in their + respective use cases. ### Backstage Security Audit -This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third party companies in auditing the platform and highlighting potential vulnerabilities (if there are any). The benefit for the adopters is clear: we want Backstage to be as much secure as possible and we want to make it reliable through a specific initiative. This initiative in particular is done together (and with the support of) the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). +This initiative is the first of a broader Security Strategy for Backstage. The +purpose of the Security Audit is to involve third party companies in auditing +the platform and highlighting potential vulnerabilities (if there are any). The +benefit for the adopters is clear: we want Backstage to be as much secure as +possible and we want to make it reliable through a specific initiative. This +initiative in particular is done together (and with the support of) the +[Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). ### Moving to Incubation in CNCF -The progress of the request can be seen [here](https://github.com/cncf/toc/pull/717). +The progress of the request can be seen +[here](https://github.com/cncf/toc/pull/717). ## Future work @@ -73,12 +96,22 @@ The following feature list doesn’t represent a commitment to develop and the list order doesn’t reflect any priority or importance. But these features are on the maintainers’ radar, with clear interest expressed by the community. -- **Backend Services:** To better scale and maintain the Backstage instances, a backend layer of services is planned to be introduced as part of the software architecture. This layer of backend services will help in decoupling the various modules (Catalog and Scaffolder just to quote some) from the front-end experience. -- **Security Plan (and Strategy):** The purpose of the Security Strategy is to move another step ahead along the path of the maturity of the platform, setting the expectations of any adopters from a security point. +- **Backend Services:** To better scale and maintain the Backstage instances, a + backend layer of services is planned to be introduced as part of the software + architecture. This layer of backend services will help in decoupling the + various modules (Catalog and Scaffolder just to quote some) from the front-end + experience. +- **Security Plan (and Strategy):** The purpose of the Security Strategy is to + move another step ahead along the path of the maturity of the platform, + setting the expectations of any adopters from a security point. - **Search GA:**. -- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query Backstage backend services with a standard query language for APIs. -- **Telemetry:** To efficiently collect, store and analyse system and application log data so that Backstage can be monitored and improved. -- **Improved UX design:** Provide a better Backstage user experience through visual guidelines and templates, especially navigation across plug-ins and portal functionalities. +- **[GraphQL](https://graphql.org/) support:** Introduce the ability to query + Backstage backend services with a standard query language for APIs. +- **Telemetry:** To efficiently collect, store and analyse system and + application log data so that Backstage can be monitored and improved. +- **Improved UX design:** Provide a better Backstage user experience through + visual guidelines and templates, especially navigation across plug-ins and + portal functionalities. ## Completed milestones From 063cbbef4785eb5b046a2512640dab1836008027 Mon Sep 17 00:00:00 2001 From: blam Date: Tue, 11 Jan 2022 13:36:57 +0100 Subject: [PATCH 056/116] chore: backtick the packages Signed-off-by: blam --- .changeset/dependabot-2af4c1d.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/dependabot-2af4c1d.md b/.changeset/dependabot-2af4c1d.md index 116df9ccd3..93dc1d1e21 100644 --- a/.changeset/dependabot-2af4c1d.md +++ b/.changeset/dependabot-2af4c1d.md @@ -3,4 +3,4 @@ '@backstage/plugin-ilert': patch --- -build(deps): bump @date-io/luxon from 1.3.13 to 2.11.1 \ No newline at end of file +build(deps): bump `@date-io/luxon` from 1.3.13 to 2.11.1 From ccd1c6617e7d5724db44168e02156d52697d0f41 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Tue, 11 Jan 2022 14:42:52 +0100 Subject: [PATCH 057/116] Update docs/overview/roadmap.md Co-authored-by: Ben Lambert --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index d95db0aaa7..870bbaeb73 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -148,7 +148,7 @@ Read more about the completed (and released) features for reference. - [Plugin marketplace](https://backstage.io/plugins) - [Improved and move documentation to backstage.io](https://backstage.io/docs/overview/what-is-backstage) - [Backstage Software Catalog (alpha)](https://backstage.io/blog/2020/06/22/backstage-service-catalog-alpha) -- [Backstage Software Templates (alpha)](https://backstage.io/blog/2020/08/05/announcing-backstage-software-templates) +- [Backstage Software Templates (beta)](https://backstage.io/blog/2021/07/26/software-templates-are-now-in-beta) - [Make it possible to add custom auth providers](https://backstage.io/blog/2020/07/01/how-to-enable-authentication-in-backstage-using-passport) - [TechDocs v0](https://github.com/backstage/backstage/milestone/15) - CI plugins: CircleCI, Jenkins, GitHub Actions and TravisCI From c870703ee413c4b23ce2d33a9516b3e8e8ab41ec Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:32:25 +0100 Subject: [PATCH 058/116] Update docs/overview/roadmap.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 870bbaeb73..bc7491a38c 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -41,7 +41,7 @@ If you have specific questions about the roadmap, please create an The Backstage roadmap lays out both [“what’s next”](#whats-next) and [“future work”](#future-work). With "next" we mean features planned for release -within the ongoing quarter starting in January until March 2022 included. With +within the ongoing quarter from January through March 2022. With "future" we mean features in the radar, but not yet scheduled. The long-term roadmap (12 - 36 months) is not detailed in the public roadmap. From f344a694a243751bb0235d78ab9a94cdc479be72 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:36:22 +0100 Subject: [PATCH 059/116] Update docs/overview/roadmap.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index bc7491a38c..31d1b56c52 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -63,7 +63,7 @@ cycle will vary based on maintainer schedules. ### Backstage 1.0 (and following versions) -During the first quarter of 2022 it is planned to be finalised and released the +During the first quarter of 2022 we plan to be finalize and release version 1.0 of the Backstage platform (defined by the Core, [Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), [Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) From ebb0cddf07708a0be94b9efd5211450aa5ebe3d1 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:38:11 +0100 Subject: [PATCH 060/116] Typo in a change --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 31d1b56c52..e49afebefc 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -63,7 +63,7 @@ cycle will vary based on maintainer schedules. ### Backstage 1.0 (and following versions) -During the first quarter of 2022 we plan to be finalize and release +During the first quarter of 2022 we plan to finalize and release version 1.0 of the Backstage platform (defined by the Core, [Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), [Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) From 51a1946c2ccc676c688c95d0a917a0feffcc31fb Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:38:47 +0100 Subject: [PATCH 061/116] Update docs/overview/roadmap.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accepting the suggestion on the release cadence. Co-authored-by: Fredrik Adelöw --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index e49afebefc..850d7167a0 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -70,7 +70,7 @@ version 1.0 of the Backstage platform (defined by the Core, and [TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview)). Included as part of this milestone: -- The cadence of minor/weekly/daily releases to provide clarity on the frequency +- Deciding on the cadence of minor/weekly/daily releases to provide clarity on the frequency and expectations for future versions of the platform and its defining modules. - The support model to set the expectations from the adopters in their respective use cases. From 2059387e4a47129c9185bc7f63a1104389ffd871 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:39:07 +0100 Subject: [PATCH 062/116] Update docs/overview/roadmap.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accepting the suggestion on the support model. Co-authored-by: Fredrik Adelöw --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 850d7167a0..ceba282bf6 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -72,7 +72,7 @@ Included as part of this milestone: - Deciding on the cadence of minor/weekly/daily releases to provide clarity on the frequency and expectations for future versions of the platform and its defining modules. -- The support model to set the expectations from the adopters in their +- Establish the support model to set the expectations from the adopters in their respective use cases. ### Backstage Security Audit From 5d7d54438c3c18a894a2b09dc2270d52672f910f Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:39:31 +0100 Subject: [PATCH 063/116] Update docs/overview/roadmap.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typo in the benefits for adopters (Accepting the suggestion). Co-authored-by: Fredrik Adelöw --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index ceba282bf6..06e094416c 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -80,7 +80,7 @@ Included as part of this milestone: This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third party companies in auditing the platform and highlighting potential vulnerabilities (if there are any). The -benefit for the adopters is clear: we want Backstage to be as much secure as +benefit for the adopters is clear: we want Backstage to be as secure as possible and we want to make it reliable through a specific initiative. This initiative in particular is done together (and with the support of) the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). From 3d6041acf78ea318a50aa5cac989ce6ffee3b4bb Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:39:49 +0100 Subject: [PATCH 064/116] Update docs/overview/roadmap.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accepting the suggestion on the security plan. Co-authored-by: Fredrik Adelöw --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 06e094416c..ceb10d3b9f 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -103,7 +103,7 @@ the maintainers’ radar, with clear interest expressed by the community. experience. - **Security Plan (and Strategy):** The purpose of the Security Strategy is to move another step ahead along the path of the maturity of the platform, - setting the expectations of any adopters from a security point. + setting the expectations of any adopters from a security standpoint. - **Search GA:**. - **[GraphQL](https://graphql.org/) support:** Introduce the ability to query Backstage backend services with a standard query language for APIs. From 7f280f3840f49da2769063874ac33795d26fd556 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Wed, 12 Jan 2022 09:41:22 +0100 Subject: [PATCH 065/116] Reviewing the telemetry description. Reviewing the telemetry description. --- docs/overview/roadmap.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index ceb10d3b9f..ad5e78bedb 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -107,8 +107,7 @@ the maintainers’ radar, with clear interest expressed by the community. - **Search GA:**. - **[GraphQL](https://graphql.org/) support:** Introduce the ability to query Backstage backend services with a standard query language for APIs. -- **Telemetry:** To efficiently collect, store and analyse system and - application log data so that Backstage can be monitored and improved. +- **Telemetry:** To efficiently generate logging and metrics in such a way that adopters can get insights so that Backstage can be monitored and improved. - **Improved UX design:** Provide a better Backstage user experience through visual guidelines and templates, especially navigation across plug-ins and portal functionalities. From 014f9a8f452d93469ff1788018086ef54b4daa36 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 6 Jan 2022 04:14:21 +0000 Subject: [PATCH 066/116] build(deps-dev): bump @types/swagger-ui-react from 3.35.2 to 4.1.1 Bumps [@types/swagger-ui-react](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/swagger-ui-react) from 3.35.2 to 4.1.1. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/swagger-ui-react) --- updated-dependencies: - dependency-name: "@types/swagger-ui-react" dependency-type: direct:development update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- plugins/api-docs/package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 8d190dae92..0d82fb2d88 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -63,7 +63,7 @@ "@testing-library/user-event": "^13.1.8", "@types/jest": "^26.0.7", "@types/node": "^14.14.32", - "@types/swagger-ui-react": "^3.23.3", + "@types/swagger-ui-react": "^4.1.1", "cross-fetch": "^3.0.6", "msw": "^0.35.0" }, diff --git a/yarn.lock b/yarn.lock index 97087652ad..c17b36f9f4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8277,10 +8277,10 @@ dependencies: "@types/superagent" "*" -"@types/swagger-ui-react@^3.23.3": - version "3.35.2" - resolved "https://registry.npmjs.org/@types/swagger-ui-react/-/swagger-ui-react-3.35.2.tgz#f362a60a0d71c2da5dc1ab420ed4aa1721e6c8a0" - integrity sha512-HA/1pw5uzn3+3gDX1R50SpzP/3GiqhUZoT5LLl8rhVu1i39Et1acz9ryuzmf5i4PoZNIfLcZYBlIok0P/6qmyA== +"@types/swagger-ui-react@^4.1.1": + version "4.1.1" + resolved "https://registry.npmjs.org/@types/swagger-ui-react/-/swagger-ui-react-4.1.1.tgz#6ae70f5b966941eb6655d79d1114d9b9ef69a980" + integrity sha512-tZqX/nShvNqtJ7WAfwOK7mphK1jqmEre4mUJh+R1RyCFVODsMTG4My2nqjXpSm8NfhVGmbFH3giD17AXmgsaJw== dependencies: "@types/react" "*" From 50f4ebc247dd4bf46dceca8216d0b3bb08e68410 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 13 Jan 2022 08:29:38 +0100 Subject: [PATCH 067/116] chore: reworking some dependencies Signed-off-by: blam --- packages/core-components/package.json | 2 +- plugins/api-docs/package.json | 3 +- yarn.lock | 105 +++++++++++++++++--------- 3 files changed, 71 insertions(+), 39 deletions(-) diff --git a/packages/core-components/package.json b/packages/core-components/package.json index 337a759da8..64938f4336 100644 --- a/packages/core-components/package.json +++ b/packages/core-components/package.json @@ -58,7 +58,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-sparklines": "^1.7.0", - "react-syntax-highlighter": "^15.4.3", + "react-syntax-highlighter": "^15.4.5", "react-text-truncate": "^0.16.0", "react-use": "^17.2.4", "react-virtualized-auto-sizer": "^1.0.6", diff --git a/plugins/api-docs/package.json b/plugins/api-docs/package.json index 0d82fb2d88..71e3dcfb66 100644 --- a/plugins/api-docs/package.json +++ b/plugins/api-docs/package.json @@ -46,8 +46,7 @@ "react-router": "6.0.0-beta.0", "react-router-dom": "6.0.0-beta.0", "react-use": "^17.2.4", - "swagger-client": "3.16.1", - "swagger-ui-react": "^4.0.0-rc.3" + "swagger-ui-react": "^4.1.3" }, "peerDependencies": { "@types/react": "^16.13.1 || ^17.0.0", diff --git a/yarn.lock b/yarn.lock index c17b36f9f4..1713059d42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2173,7 +2173,7 @@ pirates "^4.0.0" source-map-support "^0.5.16" -"@babel/runtime-corejs3@^7.10.2", "@babel/runtime-corejs3@^7.11.2", "@babel/runtime-corejs3@^7.14.7": +"@babel/runtime-corejs3@^7.10.2", "@babel/runtime-corejs3@^7.11.2": version "7.15.3" resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.15.3.tgz#28754263988198f2a928c09733ade2fb4d28089d" integrity sha512-30A3lP+sRL6ml8uhoJSs+8jwpKzbw8CqBvDc1laeptxPm5FahumJxirigcbD2qTs71Sonvj1cyZB0OKGAmxQ+A== @@ -2181,7 +2181,22 @@ core-js-pure "^3.16.0" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.6", "@babel/runtime@^7.14.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime-corejs3@^7.16.3": + version "7.16.8" + resolved "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz#ea533d96eda6fdc76b1812248e9fbd0c11d4a1a7" + integrity sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg== + dependencies: + core-js-pure "^3.20.2" + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.0", "@babel/runtime@^7.10.1", "@babel/runtime@^7.10.2", "@babel/runtime@^7.10.4", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.0", "@babel/runtime@^7.14.6", "@babel/runtime@^7.14.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.0", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": + version "7.16.3" + resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" + integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== + dependencies: + regenerator-runtime "^0.13.4" + +"@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3": version "7.16.7" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== @@ -12036,6 +12051,11 @@ core-js-pure@^3.10.2, core-js-pure@^3.16.0, core-js-pure@^3.6.5, core-js-pure@^3 resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.16.2.tgz#0ef4b79cabafb251ea86eb7d139b42bd98c533e8" integrity sha512-oxKe64UH049mJqrKkynWp6Vu0Rlm/BTXO/bJZuN2mmR3RtOFNepLlSWDd1eo16PzHpQAoNG97rLU1V/YxesJjw== +core-js-pure@^3.20.2: + version "3.20.2" + resolved "https://registry.npmjs.org/core-js-pure/-/core-js-pure-3.20.2.tgz#5d263565f0e34ceeeccdc4422fae3e84ca6b8c0f" + integrity sha512-CmWHvSKn2vNL6p6StNp1EmMIfVY/pqn3JLAjfZQ8WZGPOlGoO92EkX9/Mk81i6GxvoPXjUqEQnpM3rJ5QxxIOg== + core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: version "2.6.12" resolved "https://registry.npmjs.org/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" @@ -13393,7 +13413,7 @@ domhandler@^4.2.0: dependencies: domelementtype "^2.2.0" -dompurify@^2.2.7, dompurify@^2.2.9: +dompurify@=2.3.3, dompurify@^2.2.7, dompurify@^2.2.9: version "2.3.3" resolved "https://registry.npmjs.org/dompurify/-/dompurify-2.3.3.tgz#c1af3eb88be47324432964d8abc75cf4b98d634c" integrity sha512-dqnqRkPMAjOZE0FogZ+ceJNM2dZ3V/yNOuFB7+39qpO93hHhfRpHw3heYQC7DPK9FqbQTfBKUJhiSfz4MvXYwg== @@ -23368,11 +23388,16 @@ printj@~1.1.0: resolved "https://registry.npmjs.org/printj/-/printj-1.1.2.tgz#d90deb2975a8b9f600fb3a1c94e3f4c53c78a222" integrity sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ== -prismjs@^1.21.0, prismjs@^1.22.0, prismjs@~1.25.0: +prismjs@^1.21.0, prismjs@~1.25.0: version "1.25.0" resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.25.0.tgz#6f822df1bdad965734b310b315a23315cf999756" integrity sha512-WCjJHl1KEWbnkQom1+SzftbtXMKQoezOCYs5rECqMN+jP+apI7ftoflyqigqzopSO3hMhTEb0mFClA8lkolgEg== +prismjs@^1.25.0: + version "1.26.0" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.26.0.tgz#16881b594828bb6b45296083a8cbab46b0accd47" + integrity sha512-HUoH9C5Z3jKkl3UunCyiD5jwk0+Hz0fIgQ2nbwU2Oo/ceuTAQAg+pPVnfdt2TJWRVLcxKh9iuoYDUSc8clb5UQ== + private@^0.1.8: version "0.1.8" resolved "https://registry.npmjs.org/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" @@ -23705,6 +23730,13 @@ qs@^6.10.0, qs@^6.10.1, qs@^6.9.1, qs@^6.9.4, qs@^6.9.6: dependencies: side-channel "^1.0.4" +qs@^6.10.2: + version "6.10.3" + resolved "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e" + integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ== + dependencies: + side-channel "^1.0.4" + qs@~6.5.2: version "6.5.2" resolved "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -23720,11 +23752,6 @@ query-string@^7.0.0: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -querystring-browser@^1.0.4: - version "1.0.4" - resolved "https://registry.npmjs.org/querystring-browser/-/querystring-browser-1.0.4.tgz#f2e35881840a819bc7b1bf597faf0979e6622dc6" - integrity sha1-8uNYgYQKgZvHsb9Zf68JeeZiLcY= - querystring-es3@^0.2.0: version "0.2.1" resolved "https://registry.npmjs.org/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" @@ -23871,10 +23898,10 @@ react-colorful@^5.1.2: resolved "https://registry.npmjs.org/react-colorful/-/react-colorful-5.2.2.tgz#0a69d0648db47e51359d343854d83d250a742243" integrity sha512-Xdb1Rl6lZ5SMdNBH59eE0lGqR1g2LVD8IgPlw0WeMDrOC65lYI8fgMEwj/0dDpVRVMh5qp73ciISDst/t2O2iQ== -react-copy-to-clipboard@5.0.3: - version "5.0.3" - resolved "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.3.tgz#2a0623b1115a1d8c84144e9434d3342b5af41ab4" - integrity sha512-9S3j+m+UxDZOM0Qb8mhnT/rMR0NGSrj9A/073yz2DSxPMYhmYFBMYIdI2X4o8AjOjyFsSNxDRnCX6s/gRxpriw== +react-copy-to-clipboard@5.0.4: + version "5.0.4" + resolved "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.0.4.tgz#42ec519b03eb9413b118af92d1780c403a5f19bf" + integrity sha512-IeVAiNVKjSPeGax/Gmkqfa/+PuMTBhutEvFUaMQLwE2tS0EXrAdgOpWDX26bWTXF3HrioorR7lr08NqeYUWQCQ== dependencies: copy-to-clipboard "^3" prop-types "^15.5.8" @@ -24229,15 +24256,15 @@ react-syntax-highlighter@^13.5.3: prismjs "^1.21.0" refractor "^3.1.0" -react-syntax-highlighter@^15.4.3, react-syntax-highlighter@^15.4.4: - version "15.4.4" - resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.4.tgz#dc9043f19e7bd063ff3ea78986d22a6eaa943b2a" - integrity sha512-PsOFHNTzkb3OroXdoR897eKN5EZ6grht1iM+f1lJSq7/L0YVnkJaNVwC3wEUYPOAmeyl5xyer1DjL6MrumO6Zw== +react-syntax-highlighter@^15.4.5: + version "15.4.5" + resolved "https://registry.npmjs.org/react-syntax-highlighter/-/react-syntax-highlighter-15.4.5.tgz#db900d411d32a65c8e90c39cd64555bf463e712e" + integrity sha512-RC90KQTxZ/b7+9iE6s9nmiFLFjWswUcfULi4GwVzdFVKVMQySkJWBuOmJFfjwjMVCo0IUUuJrWebNKyviKpwLQ== dependencies: "@babel/runtime" "^7.3.1" highlight.js "^10.4.1" lowlight "^1.17.0" - prismjs "^1.22.0" + prismjs "^1.25.0" refractor "^3.2.0" react-test-renderer@^16.13.1: @@ -24609,7 +24636,7 @@ redux-immutable@^4.0.0: resolved "https://registry.npmjs.org/redux-immutable/-/redux-immutable-4.0.0.tgz#3a1a32df66366462b63691f0e1dc35e472bbc9f3" integrity sha1-Ohoy32Y2ZGK2NpHw4dw15HK7yfM= -redux@^4.0.0, redux@^4.1.0: +redux@^4.0.0: version "4.1.1" resolved "https://registry.npmjs.org/redux/-/redux-4.1.1.tgz#76f1c439bb42043f985fbd9bf21990e60bd67f47" integrity sha512-hZQZdDEM25UY2P493kPYuKqviVwZ58lEmGQNeQ+gXa+U0gYPUBf7NKYazbe3m+bs/DzM/ahN12DbF+NG8i0CWw== @@ -24624,6 +24651,13 @@ redux@^4.0.4: loose-envify "^1.4.0" symbol-observable "^1.2.0" +redux@^4.1.2: + version "4.1.2" + resolved "https://registry.npmjs.org/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" + integrity sha512-SH8PglcebESbd/shgf6mii6EIoRM0zrQyjcuQ+ojmfxjTtE0z9Y8pa62iA/OJ58qjP6j27uyW4kUF4jl/jd6sw== + dependencies: + "@babel/runtime" "^7.9.2" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" @@ -26855,39 +26889,38 @@ svgo@^2.7.0: picocolors "^1.0.0" stable "^0.1.8" -swagger-client@3.16.1, swagger-client@^3.16.1: - version "3.16.1" - resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.16.1.tgz#df86c9d407ab52c00cb356e714b0ec732bb3ad40" - integrity sha512-BcNRQzXHRGuXfhN0f80ptlr+bSaPvXwo8+gWbpmTnbKdAjcWOKAWwUx7rgGHjTKZh0qROr/GX9xOZIY8LrBuTg== +swagger-client@^3.17.0: + version "3.18.0" + resolved "https://registry.npmjs.org/swagger-client/-/swagger-client-3.18.0.tgz#2e59e666b38ded983e26fb512421ef8ff82547f0" + integrity sha512-lNfwTXHim0QiCNuZ4BKgWle7N7+9WlFLtcP02n0xSchFtdzsKJb2kWsOlwplRU3appVFjnHRy+1eVabRc3ZhbA== dependencies: "@babel/runtime-corejs3" "^7.11.2" btoa "^1.2.1" - buffer "^6.0.3" cookie "~0.4.1" cross-fetch "^3.1.4" deep-extend "~0.6.0" fast-json-patch "^3.0.0-1" form-data-encoder "^1.4.3" formdata-node "^4.0.0" + is-plain-object "^5.0.0" js-yaml "^4.1.0" lodash "^4.17.21" - qs "^6.9.4" - querystring-browser "^1.0.4" + qs "^6.10.2" traverse "~0.6.6" url "~0.11.0" -swagger-ui-react@^4.0.0-rc.3: - version "4.0.0-rc.3" - resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-4.0.0-rc.3.tgz#393f424daf55272dd36737be654d978daf870d3d" - integrity sha512-Mo3+NvwLbbPy+ZJZoQkc3UudevSM03SHTZqwZOI7EY4KyMgqeet3xElnAUGZC94GqBZTstU0rf/znkgdaNo9qg== +swagger-ui-react@^4.1.3: + version "4.1.3" + resolved "https://registry.npmjs.org/swagger-ui-react/-/swagger-ui-react-4.1.3.tgz#a722ecbe54ef237fa9080447a7c708c4c72d846a" + integrity sha512-o1AoXUTNH40cxWus0QOeWQ8x9tSIEmrLBrOgAOHDnvWJ1qyjT8PjgHjPbUVjMbja18coyuaAAeUdyLKvLGmlDA== dependencies: - "@babel/runtime-corejs3" "^7.14.7" + "@babel/runtime-corejs3" "^7.16.3" "@braintree/sanitize-url" "^5.0.2" base64-js "^1.5.1" classnames "^2.3.1" css.escape "1.5.1" deep-extend "0.6.0" - dompurify "^2.2.9" + dompurify "=2.3.3" ieee754 "^1.2.1" immutable "^3.x.x" js-file-download "^0.4.12" @@ -26896,20 +26929,20 @@ swagger-ui-react@^4.0.0-rc.3: memoizee "^0.4.15" prop-types "^15.7.2" randombytes "^2.1.0" - react-copy-to-clipboard "5.0.3" + react-copy-to-clipboard "5.0.4" react-debounce-input "=3.2.4" react-immutable-proptypes "2.2.0" react-immutable-pure-component "^2.2.0" react-inspector "^5.1.1" react-redux "^7.2.4" - react-syntax-highlighter "^15.4.4" - redux "^4.1.0" + react-syntax-highlighter "^15.4.5" + redux "^4.1.2" redux-immutable "^4.0.0" remarkable "^2.0.1" reselect "^4.0.0" serialize-error "^8.1.0" sha.js "^2.4.11" - swagger-client "^3.16.1" + swagger-client "^3.17.0" url-parse "^1.5.3" xml "=1.0.1" xml-but-prettier "^1.0.1" From 306d879536eea8cc563f9bc50ab6cc6297720afe Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 13 Jan 2022 08:43:30 +0100 Subject: [PATCH 068/116] chore: add changeset Signed-off-by: blam --- .changeset/beige-crabs-itch.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/beige-crabs-itch.md diff --git a/.changeset/beige-crabs-itch.md b/.changeset/beige-crabs-itch.md new file mode 100644 index 0000000000..9c66e42054 --- /dev/null +++ b/.changeset/beige-crabs-itch.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-api-docs': patch +--- + +chore(deps): bump `react-syntax-highligher` and `swagger-ui-react` From 2989c8282588d135888d7f5014184bc85687ebc3 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Thu, 13 Jan 2022 10:35:33 +0100 Subject: [PATCH 069/116] Avoid breaking changes Signed-off-by: Philipp Hugenroth --- .../app/src/components/search/SearchPage.tsx | 4 ++-- packages/core-components/api-report.md | 22 +++++++++---------- .../src/layout/Sidebar/Bar.test.tsx | 6 ++--- .../src/layout/Sidebar/Bar.tsx | 8 ++++--- .../src/layout/Sidebar/Items.tsx | 2 +- .../src/layout/Sidebar/MobileSidebar.tsx | 2 +- .../src/layout/Sidebar/Page.tsx | 20 +++++++++-------- .../src/layout/Sidebar/SidebarGroup.tsx | 4 ++-- .../src/layout/Sidebar/config.ts | 3 ++- .../src/layout/Sidebar/index.ts | 4 ++-- .../techdocs/src/reader/components/Reader.tsx | 4 ++-- .../General/UserSettingsAppearanceCard.tsx | 4 ++-- .../General/UserSettingsPinToggle.test.tsx | 6 ++--- .../General/UserSettingsPinToggle.tsx | 6 +++-- .../src/components/SettingsPage.tsx | 4 ++-- 15 files changed, 53 insertions(+), 46 deletions(-) diff --git a/packages/app/src/components/search/SearchPage.tsx b/packages/app/src/components/search/SearchPage.tsx index 8cd1b85d2a..9a8c208103 100644 --- a/packages/app/src/components/search/SearchPage.tsx +++ b/packages/app/src/components/search/SearchPage.tsx @@ -21,7 +21,7 @@ import { Header, Lifecycle, Page, - SidebarStateContext, + SidebarPinStateContext, } from '@backstage/core-components'; import { CatalogResultListItem } from '@backstage/plugin-catalog'; import { @@ -53,7 +53,7 @@ const useStyles = makeStyles((theme: Theme) => ({ const SearchPage = () => { const classes = useStyles(); - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return ( diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index c2244e686d..f10d7102c9 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -897,7 +897,7 @@ export const SidebarContext: Context; // @public export type SidebarContextType = { isOpen: boolean; - setOpen?: (open: boolean) => void; + setOpen: (open: boolean) => void; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1244,6 +1244,16 @@ export type SidebarPageProps = { children?: React_2.ReactNode; }; +// @public +export const SidebarPinStateContext: React_2.Context; + +// @public +export type SidebarPinStateContextType = { + isPinned: boolean; + toggleSidebarPinState: () => any; + isMobile?: boolean; +}; + // @public (undocumented) export type SidebarProps = { openDelayMs?: number; @@ -2075,16 +2085,6 @@ export const SidebarSpacer: React_2.ComponentType< // @public (undocumented) export type SidebarSpacerClassKey = 'root'; -// @public -export const SidebarStateContext: React_2.Context; - -// @public -export type SidebarStateContextType = { - isPinned: boolean; - toggleSidebarPinState: () => any; - isMobile?: boolean; -}; - // @public export const SidebarSubmenu: (props: SidebarSubmenuProps) => JSX.Element; diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index 0e0a7d5aa2..89133997f9 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -27,14 +27,14 @@ import { SidebarExpandButton, SidebarItem, SidebarSearchField, - SidebarStateContext, + SidebarPinStateContext, SidebarSubmenu, SidebarSubmenuItem, } from '.'; async function renderScalableSidebar() { await renderInTestApp( -
- , + , ); } diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index fe3964b745..ff907cdda3 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -20,7 +20,7 @@ import classnames from 'classnames'; import React, { useState, useContext, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarStateContext } from './Page'; +import { SidebarPinStateContext } from './Page'; import { MobileSidebar } from './MobileSidebar'; /** @public */ @@ -102,7 +102,9 @@ const DesktopSidebar = (props: SidebarProps) => { ); const [state, setState] = useState(State.Closed); const hoverTimerRef = useRef(); - const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); + const { isPinned, toggleSidebarPinState } = useContext( + SidebarPinStateContext, + ); const handleOpen = () => { if (isPinned || disableExpandOnHover) { @@ -185,7 +187,7 @@ const DesktopSidebar = (props: SidebarProps) => { */ export const Sidebar = (props: SidebarProps) => { const { children, openDelayMs, closeDelayMs, disableExpandOnHover } = props; - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return isMobile ? ( {children} diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index ed090690f0..b16c9ad302 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -643,7 +643,7 @@ export const SidebarExpandButton = () => { { noSsr: true }, ); - if (isSmallScreen || !setOpen) { + if (isSmallScreen) { return null; } diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 0761199c76..298c53fb8f 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -187,7 +187,7 @@ export const MobileSidebar = (props: MobileSidebarProps) => { !sidebarGroups[selectedMenuItemIndex].props.to; return ( - + {} }}> diff --git a/packages/core-components/src/layout/Sidebar/Page.tsx b/packages/core-components/src/layout/Sidebar/Page.tsx index 9e5d897de6..f23a6fa3b6 100644 --- a/packages/core-components/src/layout/Sidebar/Page.tsx +++ b/packages/core-components/src/layout/Sidebar/Page.tsx @@ -43,11 +43,11 @@ const useStyles = makeStyles( ); /** - * Type of `SidebarStateContext` + * Type of `SidebarPinStateContext` * * @public */ -export type SidebarStateContextType = { +export type SidebarPinStateContextType = { isPinned: boolean; toggleSidebarPinState: () => any; isMobile?: boolean; @@ -67,11 +67,13 @@ export type SidebarPageProps = { * * @public */ -export const SidebarStateContext = createContext({ - isPinned: true, - toggleSidebarPinState: () => {}, - isMobile: false, -}); +export const SidebarPinStateContext = createContext( + { + isPinned: true, + toggleSidebarPinState: () => {}, + isMobile: false, + }, +); export function SidebarPage(props: SidebarPageProps) { const [isPinned, setIsPinned] = useState(() => @@ -91,7 +93,7 @@ export function SidebarPage(props: SidebarPageProps) { const classes = useStyles({ isPinned }); return ( -
{props.children}
-
+ ); } diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx index fc451cd779..bc7688153a 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.tsx @@ -22,7 +22,7 @@ import BottomNavigationAction, { import { makeStyles } from '@material-ui/core/styles'; import React, { useContext } from 'react'; import { useLocation } from 'react-router-dom'; -import { SidebarStateContext } from '.'; +import { SidebarPinStateContext } from '.'; import { Link } from '../../components'; import { sidebarConfig } from './config'; import { MobileSidebarContext } from './MobileSidebar'; @@ -120,7 +120,7 @@ const MobileSidebarGroup = (props: SidebarGroupProps) => { */ export const SidebarGroup = (props: SidebarGroupProps) => { const { children, to, label, icon, value } = props; - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return isMobile ? ( diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index 0ef5282997..c41db1f537 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -63,7 +63,7 @@ export const SIDEBAR_INTRO_LOCAL_STORAGE = */ export type SidebarContextType = { isOpen: boolean; - setOpen?: (open: boolean) => void; + setOpen: (open: boolean) => void; }; /** @@ -71,6 +71,7 @@ export type SidebarContextType = { */ export const SidebarContext = createContext({ isOpen: false, + setOpen: () => {}, }); export type SidebarItemWithSubmenuContextType = { diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 61fbcd25ab..82becb667f 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -32,10 +32,10 @@ export type { export type { SidebarClassKey, SidebarProps } from './Bar'; export { SidebarPage, - SidebarStateContext as SidebarStateContext, + SidebarPinStateContext as SidebarPinStateContext, } from './Page'; export type { - SidebarStateContextType as SidebarStateContextType, + SidebarPinStateContextType as SidebarPinStateContextType, SidebarPageClassKey, SidebarPageProps, } from './Page'; diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index 424697f59d..57d2786af5 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -31,7 +31,7 @@ import { EntityName } from '@backstage/catalog-model'; import { useApi, configApiRef } from '@backstage/core-plugin-api'; import { scmIntegrationsApiRef } from '@backstage/integration-react'; import { BackstageTheme } from '@backstage/theme'; -import { SidebarStateContext } from '@backstage/core-components'; +import { SidebarPinStateContext } from '@backstage/core-components'; import { techdocsStorageApiRef } from '../../api'; @@ -146,7 +146,7 @@ export const useTechDocsReaderDom = (entityRef: EntityName): Element | null => { const [dom, setDom] = useState(null); // sidebar pinned status to be used in computing CSS style injections - const { isPinned } = useContext(SidebarStateContext); + const { isPinned } = useContext(SidebarPinStateContext); const updateSidebarPosition = useCallback(() => { if (!dom || !sidebars) return; diff --git a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx index 946d22a9f0..15ab93c6d0 100644 --- a/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsAppearanceCard.tsx @@ -13,14 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import { InfoCard, SidebarStateContext } from '@backstage/core-components'; +import { InfoCard, SidebarPinStateContext } from '@backstage/core-components'; import { List } from '@material-ui/core'; import React, { useContext } from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; import { UserSettingsThemeToggle } from './UserSettingsThemeToggle'; export const UserSettingsAppearanceCard = () => { - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return ( diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx index e2c9f23b91..b63ac85b10 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.test.tsx @@ -18,14 +18,14 @@ import { renderWithEffects, wrapInTestApp } from '@backstage/test-utils'; import { fireEvent } from '@testing-library/react'; import React from 'react'; import { UserSettingsPinToggle } from './UserSettingsPinToggle'; -import { SidebarStateContext } from '@backstage/core-components'; +import { SidebarPinStateContext } from '@backstage/core-components'; describe('', () => { it('toggles the pin sidebar button', async () => { const mockToggleFn = jest.fn(); const rendered = await renderWithEffects( wrapInTestApp( - ', () => { }} > - , + , ), ); expect(rendered.getByText('Pin Sidebar')).toBeInTheDocument(); diff --git a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx index 1cfd8b06dd..4d71df8113 100644 --- a/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx +++ b/plugins/user-settings/src/components/General/UserSettingsPinToggle.tsx @@ -22,10 +22,12 @@ import { Switch, Tooltip, } from '@material-ui/core'; -import { SidebarStateContext } from '@backstage/core-components'; +import { SidebarPinStateContext } from '@backstage/core-components'; export const UserSettingsPinToggle = () => { - const { isPinned, toggleSidebarPinState } = useContext(SidebarStateContext); + const { isPinned, toggleSidebarPinState } = useContext( + SidebarPinStateContext, + ); return ( diff --git a/plugins/user-settings/src/components/SettingsPage.tsx b/plugins/user-settings/src/components/SettingsPage.tsx index b1bfdb5fc9..88b46fc372 100644 --- a/plugins/user-settings/src/components/SettingsPage.tsx +++ b/plugins/user-settings/src/components/SettingsPage.tsx @@ -17,7 +17,7 @@ import { Header, Page, - SidebarStateContext, + SidebarPinStateContext, TabbedLayout, } from '@backstage/core-components'; import React, { useContext } from 'react'; @@ -30,7 +30,7 @@ type Props = { }; export const SettingsPage = ({ providerSettings }: Props) => { - const { isMobile } = useContext(SidebarStateContext); + const { isMobile } = useContext(SidebarPinStateContext); return ( From 07ec177dbf826971a0c6e82883b28a790c2fe436 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 13 Jan 2022 14:50:42 +0100 Subject: [PATCH 070/116] chore: restore dev-ops as we're gonna fix it Signed-off-by: blam --- packages/backend/package.json | 1 + packages/backend/src/index.ts | 3 +++ packages/backend/src/plugins/azure-devops.ts | 26 ++++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 packages/backend/src/plugins/azure-devops.ts diff --git a/packages/backend/package.json b/packages/backend/package.json index 626b496509..f204659789 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -32,6 +32,7 @@ "@backstage/integration": "^0.7.0", "@backstage/plugin-app-backend": "^0.3.21", "@backstage/plugin-auth-backend": "^0.6.0", + "@backstage/plugin-azure-devops-backend": "^0.2.6", "@backstage/plugin-badges-backend": "^0.1.14", "@backstage/plugin-catalog-backend": "^0.19.4", "@backstage/plugin-code-coverage-backend": "^0.1.18", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index b765df38f6..1942c36ad1 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -40,6 +40,7 @@ import { Config } from '@backstage/config'; import healthcheck from './plugins/healthcheck'; import { metricsInit, metricsHandler } from './metrics'; import auth from './plugins/auth'; +import azureDevOps from './plugins/azure-devops'; import catalog from './plugins/catalog'; import codeCoverage from './plugins/codecoverage'; import kubernetes from './plugins/kubernetes'; @@ -116,6 +117,7 @@ async function main() { ); const scaffolderEnv = useHotMemoize(module, () => createEnv('scaffolder')); const authEnv = useHotMemoize(module, () => createEnv('auth')); + const azureDevOpsEnv = useHotMemoize(module, () => createEnv('azure-devops')); const proxyEnv = useHotMemoize(module, () => createEnv('proxy')); const rollbarEnv = useHotMemoize(module, () => createEnv('rollbar')); const searchEnv = useHotMemoize(module, () => createEnv('search')); @@ -139,6 +141,7 @@ async function main() { apiRouter.use('/scaffolder', await scaffolder(scaffolderEnv)); apiRouter.use('/tech-insights', await techInsights(techInsightsEnv)); apiRouter.use('/auth', await auth(authEnv)); + apiRouter.use('/azure-devops', await azureDevOps(azureDevOpsEnv)); apiRouter.use('/search', await search(searchEnv)); apiRouter.use('/techdocs', await techdocs(techdocsEnv)); apiRouter.use('/todo', await todo(todoEnv)); diff --git a/packages/backend/src/plugins/azure-devops.ts b/packages/backend/src/plugins/azure-devops.ts new file mode 100644 index 0000000000..4120e655ed --- /dev/null +++ b/packages/backend/src/plugins/azure-devops.ts @@ -0,0 +1,26 @@ +/* + * 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 { createRouter } from '@backstage/plugin-azure-devops-backend'; +import { Router } from 'express'; +import type { PluginEnvironment } from '../types'; + +export default function createPlugin({ + logger, + config, +}: PluginEnvironment): Promise { + return createRouter({ logger, config }); +} From 7b8976425d070f43d0e7cbd46b3a97ee8bfd5a2c Mon Sep 17 00:00:00 2001 From: Miklos Kiss Date: Thu, 13 Jan 2022 14:22:58 +0100 Subject: [PATCH 071/116] Add clarification that Location spec is required Signed-off-by: Miklos Kiss --- docs/features/software-catalog/descriptor-format.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 57473ce4c7..2b6b2049d0 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -1266,6 +1266,9 @@ shape, this kind has the following structure. Exactly equal to `backstage.io/v1alpha1` and `Location`, respectively. +### `spec` [required] +The `spec` field is required. The miniaml spec should be an empty object. + ### `spec.type` [optional] The single location type, that's common to the targets specified in the spec. If From d78909c35e7ed51b727a4b2f6b62744bafeb8dca Mon Sep 17 00:00:00 2001 From: Miklos Kiss Date: Thu, 13 Jan 2022 15:39:52 +0100 Subject: [PATCH 072/116] Fix typo Signed-off-by: Miklos Kiss --- docs/features/software-catalog/descriptor-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 2b6b2049d0..a5f50b1998 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -1267,7 +1267,7 @@ shape, this kind has the following structure. Exactly equal to `backstage.io/v1alpha1` and `Location`, respectively. ### `spec` [required] -The `spec` field is required. The miniaml spec should be an empty object. +The `spec` field is required. The minimal spec should be an empty object. ### `spec.type` [optional] From e683a479d1b8759204cb413c78eeb9820e2db2ef Mon Sep 17 00:00:00 2001 From: Miklos Kiss Date: Thu, 13 Jan 2022 16:01:34 +0100 Subject: [PATCH 073/116] Fix formatting Signed-off-by: Miklos Kiss --- docs/features/software-catalog/descriptor-format.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index a5f50b1998..ec4e13c1d4 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -1267,6 +1267,7 @@ shape, this kind has the following structure. Exactly equal to `backstage.io/v1alpha1` and `Location`, respectively. ### `spec` [required] + The `spec` field is required. The minimal spec should be an empty object. ### `spec.type` [optional] From 3375622ec33f93d845aba01e758357f6688c3aff Mon Sep 17 00:00:00 2001 From: irma12 Date: Thu, 13 Jan 2022 17:35:53 +0100 Subject: [PATCH 074/116] Include buildkite again Signed-off-by: irma12 --- packages/app/package.json | 1 + .../app/src/components/catalog/EntityPage.tsx | 8 +++++++ yarn.lock | 23 ++++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/packages/app/package.json b/packages/app/package.json index bc30713a94..8773e441a1 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -57,6 +57,7 @@ "@roadiehq/backstage-plugin-github-insights": "^1.4.2", "@roadiehq/backstage-plugin-github-pull-requests": "^1.3.2", "@roadiehq/backstage-plugin-travis-ci": "^1.3.2", + "@roadiehq/backstage-plugin-buildkite": "^1.3.4", "history": "^5.0.0", "prop-types": "^15.7.2", "react": "^16.13.1", diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 60dc0dc979..8af61062a3 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -124,6 +124,10 @@ import { EntityTravisCIOverviewCard, isTravisciAvailable, } from '@roadiehq/backstage-plugin-travis-ci'; +import { + EntityBuildkiteContent, + isBuildkiteAvailable, +} from '@roadiehq/backstage-plugin-buildkite'; import { isNewRelicDashboardAvailable, EntityNewRelicDashboardContent, @@ -174,6 +178,10 @@ export const cicdContent = ( + + + + diff --git a/yarn.lock b/yarn.lock index c8c2cc5530..0f89165b41 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4562,7 +4562,7 @@ react-beautiful-dnd "^13.0.0" react-double-scrollbar "0.0.15" -"@material-ui/core@^4.11.0", "@material-ui/core@^4.11.3", "@material-ui/core@^4.12.2": +"@material-ui/core@^4.11.0", "@material-ui/core@^4.11.3", "@material-ui/core@^4.12.1", "@material-ui/core@^4.12.2": version "4.12.3" resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.12.3.tgz#80d665caf0f1f034e52355c5450c0e38b099d3ca" integrity sha512-sdpgI/PL56QVsEJldwEe4FFaFTLUqN+rd7sSZiRCdx2E/C7z5yK0y/khAWVBH24tXwto7I1hCzNWfJGZIYJKnw== @@ -5376,6 +5376,27 @@ resolved "https://registry.npmjs.org/@rjsf/material-ui/-/material-ui-3.2.1.tgz#84fbf322485aee3a84101e189161f0687779ec8d" integrity sha512-8UiDeDbjCImFSfOegGu13otQ7OdP9FOYpcLjeouppnhs+MPeIEAtYS+jCcBKmi3reyTagC15/KVSRhde1wS1vg== +"@roadiehq/backstage-plugin-buildkite@^1.3.4": + version "1.3.4" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-buildkite/-/backstage-plugin-buildkite-1.3.4.tgz#ca58983aa3af5bec7aa72c0f7a8433dcf725da3f" + integrity sha512-Cye44GXopAhYiC6QSx/sDgtnBOPD+D4DAydasKjOmt59NJVbgltbDsLXq9oMeFez1OphypU7lWUecVnhYFze4g== + dependencies: + "@backstage/catalog-model" "^0.9.7" + "@backstage/core-components" "^0.8.0" + "@backstage/core-plugin-api" "^0.4.0" + "@backstage/plugin-catalog-react" "^0.6.5" + "@backstage/theme" "^0.2.6" + "@material-ui/core" "^4.12.1" + "@material-ui/icons" "^4.11.2" + "@material-ui/lab" "4.0.0-alpha.57" + history "^5.0.0" + moment "^2.29.1" + react "^16.13.1" + react-dom "^16.13.1" + react-router "6.0.0-beta.0" + react-router-dom "6.0.0-beta.0" + react-use "^17.2.4" + "@roadiehq/backstage-plugin-github-insights@^1.4.2": version "1.4.2" resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-1.4.2.tgz#462e2868a1b4cee338032e4715ad797db71ebd22" From b76825924451180f7891622d8e01a13b10908023 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Fri, 7 Jan 2022 12:49:18 +0000 Subject: [PATCH 075/116] permission-backend: wrap authorize request and response batches in envelope Signed-off-by: MT Lewis --- .changeset/big-moles-visit.md | 5 + .changeset/chilled-cats-marry.md | 5 + .../src/service/router.test.ts | 489 ++++++++++-------- .../permission-backend/src/service/router.ts | 52 +- plugins/permission-common/api-report.md | 10 + .../src/PermissionClient.test.ts | 49 +- .../permission-common/src/PermissionClient.ts | 62 ++- plugins/permission-common/src/types/api.ts | 20 +- plugins/permission-common/src/types/index.ts | 2 + .../src/ServerPermissionClient.test.ts | 4 +- 10 files changed, 395 insertions(+), 303 deletions(-) create mode 100644 .changeset/big-moles-visit.md create mode 100644 .changeset/chilled-cats-marry.md diff --git a/.changeset/big-moles-visit.md b/.changeset/big-moles-visit.md new file mode 100644 index 0000000000..e1af13bc55 --- /dev/null +++ b/.changeset/big-moles-visit.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-backend': minor +--- + +**BREAKING**: Wrap batched requests and responses to /authorize in an envelope object. The latest version of the PermissionClient in @backstage/permission-common uses the new format - as long as the permission-backend is consumed using this client, no other changes are necessary. diff --git a/.changeset/chilled-cats-marry.md b/.changeset/chilled-cats-marry.md new file mode 100644 index 0000000000..2a91c2bc99 --- /dev/null +++ b/.changeset/chilled-cats-marry.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-common': minor +--- + +**BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/permission-backend. diff --git a/plugins/permission-backend/src/service/router.test.ts b/plugins/permission-backend/src/service/router.test.ts index 971cb6a223..fd09cca828 100644 --- a/plugins/permission-backend/src/service/router.test.ts +++ b/plugins/permission-backend/src/service/router.test.ts @@ -103,22 +103,24 @@ describe('createRouter', () => { it('calls the permission policy', async () => { const response = await request(app) .post('/authorize') - .send([ - { - id: '123', - permission: { - name: 'test.permission1', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission1', + attributes: {}, + }, }, - }, - { - id: '234', - permission: { - name: 'test.permission2', - attributes: {}, + { + id: '234', + permission: { + name: 'test.permission2', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(response.status).toEqual(200); @@ -141,10 +143,12 @@ describe('createRouter', () => { undefined, ); - expect(response.body).toEqual([ - { id: '123', result: AuthorizeResult.DENY }, - { id: '234', result: AuthorizeResult.DENY }, - ]); + expect(response.body).toEqual({ + items: [ + { id: '123', result: AuthorizeResult.DENY }, + { id: '234', result: AuthorizeResult.DENY }, + ], + }); }); it('resolves identity from the Authorization header', async () => { @@ -152,15 +156,17 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') .auth(token, { type: 'bearer' }) - .send([ - { - id: '123', - permission: { - name: 'test.permission', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(response.status).toEqual(200); expect(policy.handle).toHaveBeenCalledWith( @@ -172,9 +178,9 @@ describe('createRouter', () => { }, { id: 'test-user', token: 'test-token' }, ); - expect(response.body).toEqual([ - { id: '123', result: AuthorizeResult.ALLOW }, - ]); + expect(response.body).toEqual({ + items: [{ id: '123', result: AuthorizeResult.ALLOW }], + }); }); describe('conditional policy result', () => { @@ -188,27 +194,31 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') - .send([ - { - id: '123', - permission: { - name: 'test.permission', - resourceType: 'test-resource-1', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { - id: '123', - result: AuthorizeResult.CONDITIONAL, - pluginId: 'test-plugin', - resourceType: 'test-resource-1', - conditions: { rule: 'test-rule', params: ['abc'] }, - }, - ]); + expect(response.body).toEqual({ + items: [ + { + id: '123', + result: AuthorizeResult.CONDITIONAL, + pluginId: 'test-plugin', + resourceType: 'test-resource-1', + conditions: { rule: 'test-rule', params: ['abc'] }, + }, + ], + }); }); it('makes separate batched requests to multiple plugin backends', async () => { @@ -241,44 +251,46 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') .auth('test-token', { type: 'bearer' }) - .send([ - { - id: '123', - permission: { - name: 'test.permission.1', - resourceType: 'test-resource-1', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission.1', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:1', }, - resourceRef: 'resource:1', - }, - { - id: '234', - permission: { - name: 'test.permission.2', - resourceType: 'test-resource-2', - attributes: {}, + { + id: '234', + permission: { + name: 'test.permission.2', + resourceType: 'test-resource-2', + attributes: {}, + }, + resourceRef: 'resource:2', }, - resourceRef: 'resource:2', - }, - { - id: '345', - permission: { - name: 'test.permission.3', - resourceType: 'test-resource-1', - attributes: {}, + { + id: '345', + permission: { + name: 'test.permission.3', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:3', }, - resourceRef: 'resource:3', - }, - { - id: '456', - permission: { - name: 'test.permission.4', - resourceType: 'test-resource-2', - attributes: {}, + { + id: '456', + permission: { + name: 'test.permission.4', + resourceType: 'test-resource-2', + attributes: {}, + }, + resourceRef: 'resource:4', }, - resourceRef: 'resource:4', - }, - ]); + ], + }); expect(mockApplyConditions).toHaveBeenCalledWith( 'plugin-1', @@ -319,12 +331,14 @@ describe('createRouter', () => { ); expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { id: '123', result: AuthorizeResult.ALLOW }, - { id: '234', result: AuthorizeResult.ALLOW }, - { id: '345', result: AuthorizeResult.DENY }, - { id: '456', result: AuthorizeResult.DENY }, - ]); + expect(response.body).toEqual({ + items: [ + { id: '123', result: AuthorizeResult.ALLOW }, + { id: '234', result: AuthorizeResult.ALLOW }, + { id: '345', result: AuthorizeResult.DENY }, + { id: '456', result: AuthorizeResult.DENY }, + ], + }); }); it('leaves definitive results unchanged', async () => { @@ -363,60 +377,62 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') .auth('test-token', { type: 'bearer' }) - .send([ - { - id: '123', - permission: { - name: 'test.permission.1', - resourceType: 'test-resource-1', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission.1', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:1', }, - resourceRef: 'resource:1', - }, - { - id: '234', - permission: { - name: 'test.permission.2', - resourceType: 'test-resource-2', - attributes: {}, + { + id: '234', + permission: { + name: 'test.permission.2', + resourceType: 'test-resource-2', + attributes: {}, + }, + resourceRef: 'resource:2', }, - resourceRef: 'resource:2', - }, - { - id: '345', - permission: { - name: 'test.permission.3', - resourceType: 'test-resource-1', - attributes: {}, + { + id: '345', + permission: { + name: 'test.permission.3', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:3', }, - resourceRef: 'resource:3', - }, - { - id: '456', - permission: { - name: 'test.permission.4', - resourceType: 'test-resource-1', - attributes: {}, + { + id: '456', + permission: { + name: 'test.permission.4', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:4', }, - resourceRef: 'resource:4', - }, - { - id: '567', - permission: { - name: 'test.permission.5', - resourceType: 'test-resource-2', - attributes: {}, + { + id: '567', + permission: { + name: 'test.permission.5', + resourceType: 'test-resource-2', + attributes: {}, + }, + resourceRef: 'resource:5', }, - resourceRef: 'resource:5', - }, - { - id: '678', - permission: { - name: 'test.permission.6', - attributes: {}, + { + id: '678', + permission: { + name: 'test.permission.6', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(mockApplyConditions).toHaveBeenCalledWith( 'plugin-1', @@ -457,14 +473,16 @@ describe('createRouter', () => { ); expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { id: '123', result: AuthorizeResult.DENY }, - { id: '234', result: AuthorizeResult.DENY }, - { id: '345', result: AuthorizeResult.ALLOW }, - { id: '456', result: AuthorizeResult.ALLOW }, - { id: '567', result: AuthorizeResult.ALLOW }, - { id: '678', result: AuthorizeResult.DENY }, - ]); + expect(response.body).toEqual({ + items: [ + { id: '123', result: AuthorizeResult.DENY }, + { id: '234', result: AuthorizeResult.DENY }, + { id: '345', result: AuthorizeResult.ALLOW }, + { id: '456', result: AuthorizeResult.ALLOW }, + { id: '567', result: AuthorizeResult.ALLOW }, + { id: '678', result: AuthorizeResult.DENY }, + ], + }); }); it('leaves conditional results without resourceRefs unchanged', async () => { @@ -494,43 +512,45 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') .auth('test-token', { type: 'bearer' }) - .send([ - { - id: '123', - permission: { - name: 'test.permission.1', - resourceType: 'test-resource-1', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission.1', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:1', }, - resourceRef: 'resource:1', - }, - { - id: '234', - permission: { - name: 'test.permission.2', - resourceType: 'test-resource-2', - attributes: {}, + { + id: '234', + permission: { + name: 'test.permission.2', + resourceType: 'test-resource-2', + attributes: {}, + }, + resourceRef: 'resource:2', }, - resourceRef: 'resource:2', - }, - { - id: '345', - permission: { - name: 'test.permission.3', - resourceType: 'test-resource-1', - attributes: {}, + { + id: '345', + permission: { + name: 'test.permission.3', + resourceType: 'test-resource-1', + attributes: {}, + }, + resourceRef: 'resource:3', }, - resourceRef: 'resource:3', - }, - { - id: '456', - permission: { - name: 'test.permission.4', - resourceType: 'test-resource-1', - attributes: {}, + { + id: '456', + permission: { + name: 'test.permission.4', + resourceType: 'test-resource-1', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(mockApplyConditions).toHaveBeenCalledWith( 'plugin-1', @@ -559,18 +579,20 @@ describe('createRouter', () => { ); expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { id: '123', result: AuthorizeResult.ALLOW }, - { id: '234', result: AuthorizeResult.ALLOW }, - { id: '345', result: AuthorizeResult.ALLOW }, - { - id: '456', - result: AuthorizeResult.CONDITIONAL, - pluginId: 'plugin-1', - resourceType: 'test-resource-1', - conditions: { rule: 'test-rule', params: ['abc'] }, - }, - ]); + expect(response.body).toEqual({ + items: [ + { id: '123', result: AuthorizeResult.ALLOW }, + { id: '234', result: AuthorizeResult.ALLOW }, + { id: '345', result: AuthorizeResult.ALLOW }, + { + id: '456', + result: AuthorizeResult.CONDITIONAL, + pluginId: 'plugin-1', + resourceType: 'test-resource-1', + conditions: { rule: 'test-rule', params: ['abc'] }, + }, + ], + }); }); it.each<[ApplyConditionsResponseEntry['result'], string]>([ @@ -600,26 +622,28 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') .auth('test-token', { type: 'bearer' }) - .send([ - { - id: '123', - resourceRef: 'test/resource', - permission: { - name: 'test.permission', - resourceType: 'test-resource-1', - attributes: {}, + .send({ + items: [ + { + id: '123', + resourceRef: 'test/resource', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, }, - }, - { - id: '234', - resourceRef: 'test/resource', - permission: { - name: 'test.permission', - resourceType: 'test-resource-1', - attributes: {}, + { + id: '234', + resourceRef: 'test/resource', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(mockApplyConditions).toHaveBeenCalledWith( 'test-plugin', @@ -641,16 +665,18 @@ describe('createRouter', () => { ); expect(response.status).toEqual(200); - expect(response.body).toEqual([ - { - id: '123', - result, - }, - { - id: '234', - result, - }, - ]); + expect(response.body).toEqual({ + items: [ + { + id: '123', + result, + }, + { + id: '234', + result, + }, + ], + }); }, ); }); @@ -660,9 +686,14 @@ describe('createRouter', () => { '', {}, [{ permission: { name: 'test.permission', attributes: {} } }], - [{ id: '123' }], - [{ id: '123', permission: { name: 'test.permission' } }], - [{ id: '123', permission: { attributes: { invalid: 'attribute' } } }], + { items: [{ permission: { name: 'test.permission', attributes: {} } }] }, + { items: [{ id: '123' }] }, + { items: [{ id: '123', permission: { name: 'test.permission' } }] }, + { + items: [ + { id: '123', permission: { attributes: { invalid: 'attribute' } } }, + ], + }, ])('returns a 400 error for invalid request %#', async requestBody => { const response = await request(app).post('/authorize').send(requestBody); @@ -686,16 +717,18 @@ describe('createRouter', () => { const response = await request(app) .post('/authorize') - .send([ - { - id: '123', - permission: { - name: 'test.permission', - resourceType: 'test-resource-1', - attributes: {}, + .send({ + items: [ + { + id: '123', + permission: { + name: 'test.permission', + resourceType: 'test-resource-1', + attributes: {}, + }, }, - }, - ]); + ], + }); expect(response.status).toEqual(500); expect(response.body).toEqual( diff --git a/plugins/permission-backend/src/service/router.ts b/plugins/permission-backend/src/service/router.ts index 58438afbb5..c504fdbe3a 100644 --- a/plugins/permission-backend/src/service/router.ts +++ b/plugins/permission-backend/src/service/router.ts @@ -32,6 +32,8 @@ import { AuthorizeResponse, AuthorizeRequest, Identified, + AuthorizeRequestEnvelope, + AuthorizeResponseEnvelope, } from '@backstage/plugin-permission-common'; import { ApplyConditionsRequestEntry, @@ -42,26 +44,28 @@ import { PermissionIntegrationClient } from './PermissionIntegrationClient'; import { memoize } from 'lodash'; import DataLoader from 'dataloader'; -const requestSchema: z.ZodSchema[]> = z.array( - z.object({ - id: z.string(), - resourceRef: z.string().optional(), - permission: z.object({ - name: z.string(), - resourceType: z.string().optional(), - attributes: z.object({ - action: z - .union([ - z.literal('create'), - z.literal('read'), - z.literal('update'), - z.literal('delete'), - ]) - .optional(), +const requestSchema: z.ZodSchema = z.object({ + items: z.array( + z.object({ + id: z.string(), + resourceRef: z.string().optional(), + permission: z.object({ + name: z.string(), + resourceType: z.string().optional(), + attributes: z.object({ + action: z + .union([ + z.literal('create'), + z.literal('read'), + z.literal('update'), + z.literal('delete'), + ]) + .optional(), + }), }), }), - }), -); + ), +}); /** * Options required when constructing a new {@link express#Router} using @@ -150,8 +154,8 @@ export async function createRouter( router.post( '/authorize', async ( - req: Request[]>, - res: Response[]>, + req: Request, + res: Response, ) => { const token = IdentityClient.getBearerToken(req.header('authorization')); const user = token ? await identity.authenticate(token) : undefined; @@ -164,15 +168,15 @@ export async function createRouter( const body = parseResult.data; - res.json( - await handleRequest( - body, + res.json({ + items: await handleRequest( + body.items, user, policy, permissionIntegrationClient, req.header('authorization'), ), - ); + }); }, ); diff --git a/plugins/permission-common/api-report.md b/plugins/permission-common/api-report.md index 70795d14ac..3c681cc46f 100644 --- a/plugins/permission-common/api-report.md +++ b/plugins/permission-common/api-report.md @@ -11,6 +11,11 @@ export type AuthorizeRequest = { resourceRef?: string; }; +// @public +export type AuthorizeRequestEnvelope = { + items: Identified[]; +}; + // @public export type AuthorizeRequestOptions = { token?: string; @@ -26,6 +31,11 @@ export type AuthorizeResponse = conditions: PermissionCriteria; }; +// @public +export type AuthorizeResponseEnvelope = { + items: Identified[]; +}; + // @public export enum AuthorizeResult { ALLOW = 'ALLOW', diff --git a/plugins/permission-common/src/PermissionClient.test.ts b/plugins/permission-common/src/PermissionClient.test.ts index a432735a5e..0cd6a09af9 100644 --- a/plugins/permission-common/src/PermissionClient.test.ts +++ b/plugins/permission-common/src/PermissionClient.test.ts @@ -54,12 +54,14 @@ describe('PermissionClient', () => { describe('authorize', () => { const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => { - const responses = req.body.map((a: Identified) => ({ - id: a.id, - result: AuthorizeResult.ALLOW, - })); + const responses = req.body.items.map( + (a: Identified) => ({ + id: a.id, + result: AuthorizeResult.ALLOW, + }), + ); - return res(json(responses)); + return res(json({ items: responses })); }); beforeEach(() => { @@ -79,12 +81,15 @@ describe('PermissionClient', () => { await client.authorize([mockAuthorizeRequest]); const request = mockAuthorizeHandler.mock.calls[0][0]; - expect(request.body[0]).toEqual( - expect.objectContaining({ - permission: mockPermission, - resourceRef: 'foo', - }), - ); + + expect(request.body).toEqual({ + items: [ + expect.objectContaining({ + permission: mockPermission, + resourceRef: 'foo', + }), + ], + }); }); it('should return the response from the fetch request', async () => { @@ -122,7 +127,11 @@ describe('PermissionClient', () => { it('should reject responses with missing ids', async () => { mockAuthorizeHandler.mockImplementationOnce( (_req, res, { json }: RestContext) => { - return res(json([{ id: 'wrong-id', result: AuthorizeResult.ALLOW }])); + return res( + json({ + items: [{ id: 'wrong-id', result: AuthorizeResult.ALLOW }], + }), + ); }, ); await expect( @@ -133,12 +142,14 @@ describe('PermissionClient', () => { it('should reject invalid responses', async () => { mockAuthorizeHandler.mockImplementationOnce( (req, res, { json }: RestContext) => { - const responses = req.body.map((a: Identified) => ({ - id: a.id, - outcome: AuthorizeResult.ALLOW, - })); + const responses = req.body.items.map( + (a: Identified) => ({ + id: a.id, + outcome: AuthorizeResult.ALLOW, + }), + ); - return res(json(responses)); + return res(json({ items: responses })); }, ); await expect( @@ -151,10 +162,10 @@ describe('PermissionClient', () => { (req, res, { json }: RestContext) => { const responses = req.body.map((a: Identified) => ({ id: a.id, - outcome: AuthorizeResult.DENY, + result: AuthorizeResult.DENY, })); - return res(json(responses)); + return res(json({ items: responses })); }, ); const disabled = new PermissionClient({ diff --git a/plugins/permission-common/src/PermissionClient.ts b/plugins/permission-common/src/PermissionClient.ts index 62e5ec8172..6e45bdc072 100644 --- a/plugins/permission-common/src/PermissionClient.ts +++ b/plugins/permission-common/src/PermissionClient.ts @@ -26,6 +26,8 @@ import { Identified, PermissionCriteria, PermissionCondition, + AuthorizeResponseEnvelope, + AuthorizeRequestEnvelope, } from './types/api'; import { DiscoveryApi } from './types/discovery'; import { @@ -46,22 +48,24 @@ const permissionCriteriaSchema: z.ZodSchema< .or(z.object({ not: permissionCriteriaSchema })), ); -const responseSchema = z.array( - z - .object({ - id: z.string(), - result: z - .literal(AuthorizeResult.ALLOW) - .or(z.literal(AuthorizeResult.DENY)), - }) - .or( - z.object({ +const responseSchema = z.object({ + items: z.array( + z + .object({ id: z.string(), - result: z.literal(AuthorizeResult.CONDITIONAL), - conditions: permissionCriteriaSchema, - }), - ), -); + result: z + .literal(AuthorizeResult.ALLOW) + .or(z.literal(AuthorizeResult.DENY)), + }) + .or( + z.object({ + id: z.string(), + result: z.literal(AuthorizeResult.CONDITIONAL), + conditions: permissionCriteriaSchema, + }), + ), + ), +}); /** * An isomorphic client for requesting authorization for Backstage permissions. @@ -106,17 +110,17 @@ export class PermissionClient implements PermissionAuthorizer { return requests.map(_ => ({ result: AuthorizeResult.ALLOW })); } - const identifiedRequests: Identified[] = requests.map( - request => ({ + const requestEnvelope: AuthorizeRequestEnvelope = { + items: requests.map(request => ({ id: uuid.v4(), ...request, - }), - ); + })), + }; const permissionApi = await this.discovery.getBaseUrl('permission'); const response = await fetch(`${permissionApi}/authorize`, { method: 'POST', - body: JSON.stringify(identifiedRequests), + body: JSON.stringify(requestEnvelope), headers: { ...this.getAuthorizationHeader(options?.token), 'content-type': 'application/json', @@ -126,15 +130,15 @@ export class PermissionClient implements PermissionAuthorizer { throw await ResponseError.fromResponse(response); } - const identifiedResponses = await response.json(); - this.assertValidResponses(identifiedRequests, identifiedResponses); + const responseEnvelope = await response.json(); + this.assertValidResponses(requestEnvelope, responseEnvelope); - const responsesById = identifiedResponses.reduce((acc, r) => { + const responsesById = responseEnvelope.items.reduce((acc, r) => { acc[r.id] = r; return acc; }, {} as Record>); - return identifiedRequests.map(request => responsesById[request.id]); + return requestEnvelope.items.map(request => responsesById[request.id]); } private getAuthorizationHeader(token?: string): Record { @@ -142,12 +146,14 @@ export class PermissionClient implements PermissionAuthorizer { } private assertValidResponses( - requests: Identified[], + requestEnvelope: AuthorizeRequestEnvelope, json: any, - ): asserts json is Identified[] { + ): asserts json is AuthorizeResponseEnvelope { const authorizedResponses = responseSchema.parse(json); - const responseIds = authorizedResponses.map(r => r.id); - const hasAllRequestIds = requests.every(r => responseIds.includes(r.id)); + const responseIds = authorizedResponses.items.map(r => r.id); + const hasAllRequestIds = requestEnvelope.items.every(r => + responseIds.includes(r.id), + ); if (!hasAllRequestIds) { throw new Error( 'Unexpected authorization response from permission-backend', diff --git a/plugins/permission-common/src/types/api.ts b/plugins/permission-common/src/types/api.ts index 7f4cd0730b..abdae021d0 100644 --- a/plugins/permission-common/src/types/api.ts +++ b/plugins/permission-common/src/types/api.ts @@ -43,7 +43,7 @@ export enum AuthorizeResult { } /** - * An authorization request for {@link PermissionClient#authorize}. + * An individual authorization request for {@link PermissionClient#authorize}. * @public */ export type AuthorizeRequest = { @@ -51,6 +51,14 @@ export type AuthorizeRequest = { resourceRef?: string; }; +/** + * A batch of authorization requests from {@link PermissionClient#authorize}. + * @public + */ +export type AuthorizeRequestEnvelope = { + items: Identified[]; +}; + /** * A condition returned with a CONDITIONAL authorization response. * @@ -75,7 +83,7 @@ export type PermissionCriteria = | TQuery; /** - * An authorization response from {@link PermissionClient#authorize}. + * An individual authorization response from {@link PermissionClient#authorize}. * @public */ export type AuthorizeResponse = @@ -84,3 +92,11 @@ export type AuthorizeResponse = result: AuthorizeResult.CONDITIONAL; conditions: PermissionCriteria; }; + +/** + * A batch of authorization responses from {@link PermissionClient#authorize}. + * @public + */ +export type AuthorizeResponseEnvelope = { + items: Identified[]; +}; diff --git a/plugins/permission-common/src/types/index.ts b/plugins/permission-common/src/types/index.ts index 583a3577bd..9fbe911941 100644 --- a/plugins/permission-common/src/types/index.ts +++ b/plugins/permission-common/src/types/index.ts @@ -17,7 +17,9 @@ export { AuthorizeResult } from './api'; export type { AuthorizeRequest, + AuthorizeRequestEnvelope, AuthorizeResponse, + AuthorizeResponseEnvelope, Identified, PermissionCondition, PermissionCriteria, diff --git a/plugins/permission-node/src/ServerPermissionClient.test.ts b/plugins/permission-node/src/ServerPermissionClient.test.ts index 2c29ef8939..4864aa4606 100644 --- a/plugins/permission-node/src/ServerPermissionClient.test.ts +++ b/plugins/permission-node/src/ServerPermissionClient.test.ts @@ -32,12 +32,12 @@ import { RestContext, rest } from 'msw'; const server = setupServer(); const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => { - const responses = req.body.map((r: Identified) => ({ + const responses = req.body.items.map((r: Identified) => ({ id: r.id, result: AuthorizeResult.ALLOW, })); - return res(json(responses)); + return res(json({ items: responses })); }); const mockBaseUrl = 'http://backstage:9191/i-am-a-mock-base'; const discovery: PluginEndpointDiscovery = { From 0ae4f4cc82dc0303fac95fd06cdb9cad7d56e8ab Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Thu, 13 Jan 2022 13:39:15 +0000 Subject: [PATCH 076/116] permissions: rename authorize request and response types to avoid envelope suffix Signed-off-by: MT Lewis --- .changeset/afraid-gorillas-beg.md | 5 ++ .changeset/chilled-cats-marry.md | 2 + .changeset/pink-actors-poke.md | 6 ++ .../permission-backend/src/service/router.ts | 56 +++++++++---------- plugins/permission-common/api-report.md | 46 +++++++-------- .../src/PermissionClient.test.ts | 40 +++++++------ .../permission-common/src/PermissionClient.ts | 40 ++++++------- plugins/permission-common/src/types/api.ts | 12 ++-- plugins/permission-common/src/types/index.ts | 4 +- .../permission-common/src/types/permission.ts | 6 +- plugins/permission-node/api-report.md | 12 ++-- .../src/ServerPermissionClient.test.ts | 4 +- .../src/ServerPermissionClient.ts | 12 ++-- plugins/permission-node/src/policy/index.ts | 2 +- plugins/permission-node/src/policy/types.ts | 10 ++-- plugins/permission-node/src/types.ts | 2 +- plugins/permission-react/api-report.md | 8 +-- .../src/apis/IdentityPermissionApi.ts | 6 +- .../src/apis/PermissionApi.ts | 6 +- 19 files changed, 145 insertions(+), 134 deletions(-) create mode 100644 .changeset/afraid-gorillas-beg.md create mode 100644 .changeset/pink-actors-poke.md diff --git a/.changeset/afraid-gorillas-beg.md b/.changeset/afraid-gorillas-beg.md new file mode 100644 index 0000000000..fdaf03502a --- /dev/null +++ b/.changeset/afraid-gorillas-beg.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-permission-react': minor +--- + +**BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common. diff --git a/.changeset/chilled-cats-marry.md b/.changeset/chilled-cats-marry.md index 2a91c2bc99..9c7617de33 100644 --- a/.changeset/chilled-cats-marry.md +++ b/.changeset/chilled-cats-marry.md @@ -2,4 +2,6 @@ '@backstage/plugin-permission-common': minor --- +**BREAKING**: Authorize API request and response types have been updated. The existing `AuthorizeRequest` and `AuthorizeResponse` types now match the entire request and response objects for the /authorize endpoint, and new types `AuthorizeQuery` and `AuthorizeDecision` have been introduced for individual items in the request and response batches respectively. + **BREAKING**: PermissionClient has been updated to use the new request and response format in the latest version of @backstage/permission-backend. diff --git a/.changeset/pink-actors-poke.md b/.changeset/pink-actors-poke.md new file mode 100644 index 0000000000..e35a925e8c --- /dev/null +++ b/.changeset/pink-actors-poke.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-permission-node': minor +--- + +**BREAKING**: `PolicyAuthorizeRequest` type has been renamed to `PolicyAuthorizeQuery`. +**BREAKING**: Update to use renamed request and response types from @backstage/plugin-permission-common. diff --git a/plugins/permission-backend/src/service/router.ts b/plugins/permission-backend/src/service/router.ts index c504fdbe3a..e22aec8129 100644 --- a/plugins/permission-backend/src/service/router.ts +++ b/plugins/permission-backend/src/service/router.ts @@ -29,11 +29,11 @@ import { } from '@backstage/plugin-auth-backend'; import { AuthorizeResult, - AuthorizeResponse, - AuthorizeRequest, + AuthorizeDecision, + AuthorizeQuery, Identified, - AuthorizeRequestEnvelope, - AuthorizeResponseEnvelope, + AuthorizeRequest, + AuthorizeResponse, } from '@backstage/plugin-permission-common'; import { ApplyConditionsRequestEntry, @@ -44,27 +44,27 @@ import { PermissionIntegrationClient } from './PermissionIntegrationClient'; import { memoize } from 'lodash'; import DataLoader from 'dataloader'; -const requestSchema: z.ZodSchema = z.object({ - items: z.array( - z.object({ - id: z.string(), - resourceRef: z.string().optional(), - permission: z.object({ - name: z.string(), - resourceType: z.string().optional(), - attributes: z.object({ - action: z - .union([ - z.literal('create'), - z.literal('read'), - z.literal('update'), - z.literal('delete'), - ]) - .optional(), - }), - }), +const querySchema: z.ZodSchema> = z.object({ + id: z.string(), + resourceRef: z.string().optional(), + permission: z.object({ + name: z.string(), + resourceType: z.string().optional(), + attributes: z.object({ + action: z + .union([ + z.literal('create'), + z.literal('read'), + z.literal('update'), + z.literal('delete'), + ]) + .optional(), }), - ), + }), +}); + +const requestSchema: z.ZodSchema = z.object({ + items: z.array(querySchema), }); /** @@ -81,12 +81,12 @@ export interface RouterOptions { } const handleRequest = async ( - requests: Identified[], + requests: Identified[], user: BackstageIdentityResponse | undefined, policy: PermissionPolicy, permissionIntegrationClient: PermissionIntegrationClient, authHeader?: string, -): Promise[]> => { +): Promise[]> => { const applyConditionsLoaderFor = memoize((pluginId: string) => { return new DataLoader< ApplyConditionsRequestEntry, @@ -154,8 +154,8 @@ export async function createRouter( router.post( '/authorize', async ( - req: Request, - res: Response, + req: Request, + res: Response, ) => { const token = IdentityClient.getBearerToken(req.header('authorization')); const user = token ? await identity.authenticate(token) : undefined; diff --git a/plugins/permission-common/api-report.md b/plugins/permission-common/api-report.md index 3c681cc46f..30a67e8c8b 100644 --- a/plugins/permission-common/api-report.md +++ b/plugins/permission-common/api-report.md @@ -6,23 +6,7 @@ import { Config } from '@backstage/config'; // @public -export type AuthorizeRequest = { - permission: Permission; - resourceRef?: string; -}; - -// @public -export type AuthorizeRequestEnvelope = { - items: Identified[]; -}; - -// @public -export type AuthorizeRequestOptions = { - token?: string; -}; - -// @public -export type AuthorizeResponse = +export type AuthorizeDecision = | { result: AuthorizeResult.ALLOW | AuthorizeResult.DENY; } @@ -32,8 +16,24 @@ export type AuthorizeResponse = }; // @public -export type AuthorizeResponseEnvelope = { - items: Identified[]; +export type AuthorizeQuery = { + permission: Permission; + resourceRef?: string; +}; + +// @public +export type AuthorizeRequest = { + items: Identified[]; +}; + +// @public +export type AuthorizeRequestOptions = { + token?: string; +}; + +// @public +export type AuthorizeResponse = { + items: Identified[]; }; // @public @@ -81,18 +81,18 @@ export type PermissionAttributes = { export interface PermissionAuthorizer { // (undocumented) authorize( - requests: AuthorizeRequest[], + queries: AuthorizeQuery[], options?: AuthorizeRequestOptions, - ): Promise; + ): Promise; } // @public export class PermissionClient implements PermissionAuthorizer { constructor(options: { discovery: DiscoveryApi; config: Config }); authorize( - requests: AuthorizeRequest[], + queries: AuthorizeQuery[], options?: AuthorizeRequestOptions, - ): Promise; + ): Promise; } // @public diff --git a/plugins/permission-common/src/PermissionClient.test.ts b/plugins/permission-common/src/PermissionClient.test.ts index 0cd6a09af9..0e57f618b7 100644 --- a/plugins/permission-common/src/PermissionClient.test.ts +++ b/plugins/permission-common/src/PermissionClient.test.ts @@ -18,7 +18,7 @@ import { RestContext, rest } from 'msw'; import { setupServer } from 'msw/node'; import { ConfigReader } from '@backstage/config'; import { PermissionClient } from './PermissionClient'; -import { AuthorizeRequest, AuthorizeResult, Identified } from './types/api'; +import { AuthorizeQuery, AuthorizeResult, Identified } from './types/api'; import { DiscoveryApi } from './types/discovery'; import { Permission } from './types/permission'; @@ -42,7 +42,7 @@ const mockPermission: Permission = { resourceType: 'test-resource', }; -const mockAuthorizeRequest = { +const mockAuthorizeQuery = { permission: mockPermission, resourceRef: 'foo', }; @@ -54,12 +54,10 @@ describe('PermissionClient', () => { describe('authorize', () => { const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => { - const responses = req.body.items.map( - (a: Identified) => ({ - id: a.id, - result: AuthorizeResult.ALLOW, - }), - ); + const responses = req.body.items.map((a: Identified) => ({ + id: a.id, + result: AuthorizeResult.ALLOW, + })); return res(json({ items: responses })); }); @@ -73,12 +71,12 @@ describe('PermissionClient', () => { }); it('should fetch entities from correct endpoint', async () => { - await client.authorize([mockAuthorizeRequest]); + await client.authorize([mockAuthorizeQuery]); expect(mockAuthorizeHandler).toHaveBeenCalled(); }); it('should include a request body', async () => { - await client.authorize([mockAuthorizeRequest]); + await client.authorize([mockAuthorizeQuery]); const request = mockAuthorizeHandler.mock.calls[0][0]; @@ -93,21 +91,21 @@ describe('PermissionClient', () => { }); it('should return the response from the fetch request', async () => { - const response = await client.authorize([mockAuthorizeRequest]); + const response = await client.authorize([mockAuthorizeQuery]); expect(response[0]).toEqual( expect.objectContaining({ result: AuthorizeResult.ALLOW }), ); }); it('should not include authorization headers if no token is supplied', async () => { - await client.authorize([mockAuthorizeRequest]); + await client.authorize([mockAuthorizeQuery]); const request = mockAuthorizeHandler.mock.calls[0][0]; expect(request.headers.has('authorization')).toEqual(false); }); it('should include correctly-constructed authorization header if token is supplied', async () => { - await client.authorize([mockAuthorizeRequest], { token }); + await client.authorize([mockAuthorizeQuery], { token }); const request = mockAuthorizeHandler.mock.calls[0][0]; expect(request.headers.get('authorization')).toEqual('Bearer fake-token'); @@ -120,7 +118,7 @@ describe('PermissionClient', () => { }, ); await expect( - client.authorize([mockAuthorizeRequest], { token }), + client.authorize([mockAuthorizeQuery], { token }), ).rejects.toThrowError(/request failed with 401/i); }); @@ -135,7 +133,7 @@ describe('PermissionClient', () => { }, ); await expect( - client.authorize([mockAuthorizeRequest], { token }), + client.authorize([mockAuthorizeQuery], { token }), ).rejects.toThrowError(/Unexpected authorization response/i); }); @@ -143,7 +141,7 @@ describe('PermissionClient', () => { mockAuthorizeHandler.mockImplementationOnce( (req, res, { json }: RestContext) => { const responses = req.body.items.map( - (a: Identified) => ({ + (a: Identified) => ({ id: a.id, outcome: AuthorizeResult.ALLOW, }), @@ -153,14 +151,14 @@ describe('PermissionClient', () => { }, ); await expect( - client.authorize([mockAuthorizeRequest], { token }), + client.authorize([mockAuthorizeQuery], { token }), ).rejects.toThrowError(/invalid input/i); }); it('should allow all when permission.enabled is false', async () => { mockAuthorizeHandler.mockImplementationOnce( (req, res, { json }: RestContext) => { - const responses = req.body.map((a: Identified) => ({ + const responses = req.body.map((a: Identified) => ({ id: a.id, result: AuthorizeResult.DENY, })); @@ -172,7 +170,7 @@ describe('PermissionClient', () => { discovery, config: new ConfigReader({ permission: { enabled: false } }), }); - const response = await disabled.authorize([mockAuthorizeRequest]); + const response = await disabled.authorize([mockAuthorizeQuery]); expect(response[0]).toEqual( expect.objectContaining({ result: AuthorizeResult.ALLOW }), ); @@ -182,7 +180,7 @@ describe('PermissionClient', () => { it('should allow all when permission.enabled is not configured', async () => { mockAuthorizeHandler.mockImplementationOnce( (req, res, { json }: RestContext) => { - const responses = req.body.map((a: Identified) => ({ + const responses = req.body.map((a: Identified) => ({ id: a.id, outcome: AuthorizeResult.DENY, })); @@ -194,7 +192,7 @@ describe('PermissionClient', () => { discovery, config: new ConfigReader({}), }); - const response = await disabled.authorize([mockAuthorizeRequest]); + const response = await disabled.authorize([mockAuthorizeQuery]); expect(response[0]).toEqual( expect.objectContaining({ result: AuthorizeResult.ALLOW }), ); diff --git a/plugins/permission-common/src/PermissionClient.ts b/plugins/permission-common/src/PermissionClient.ts index 6e45bdc072..58f4d44d20 100644 --- a/plugins/permission-common/src/PermissionClient.ts +++ b/plugins/permission-common/src/PermissionClient.ts @@ -21,13 +21,13 @@ import * as uuid from 'uuid'; import { z } from 'zod'; import { AuthorizeResult, - AuthorizeRequest, - AuthorizeResponse, + AuthorizeQuery, + AuthorizeDecision, Identified, PermissionCriteria, PermissionCondition, - AuthorizeResponseEnvelope, - AuthorizeRequestEnvelope, + AuthorizeResponse, + AuthorizeRequest, } from './types/api'; import { DiscoveryApi } from './types/discovery'; import { @@ -98,29 +98,29 @@ export class PermissionClient implements PermissionAuthorizer { * @public */ async authorize( - requests: AuthorizeRequest[], + queries: AuthorizeQuery[], options?: AuthorizeRequestOptions, - ): Promise { + ): Promise { // TODO(permissions): it would be great to provide some kind of typing guarantee that // conditional responses will only ever be returned for requests containing a resourceType // but no resourceRef. That way clients who aren't prepared to handle filtering according // to conditions can be guaranteed that they won't unexpectedly get a CONDITIONAL response. if (!this.enabled) { - return requests.map(_ => ({ result: AuthorizeResult.ALLOW })); + return queries.map(_ => ({ result: AuthorizeResult.ALLOW })); } - const requestEnvelope: AuthorizeRequestEnvelope = { - items: requests.map(request => ({ + const request: AuthorizeRequest = { + items: queries.map(query => ({ id: uuid.v4(), - ...request, + ...query, })), }; const permissionApi = await this.discovery.getBaseUrl('permission'); const response = await fetch(`${permissionApi}/authorize`, { method: 'POST', - body: JSON.stringify(requestEnvelope), + body: JSON.stringify(request), headers: { ...this.getAuthorizationHeader(options?.token), 'content-type': 'application/json', @@ -130,28 +130,28 @@ export class PermissionClient implements PermissionAuthorizer { throw await ResponseError.fromResponse(response); } - const responseEnvelope = await response.json(); - this.assertValidResponses(requestEnvelope, responseEnvelope); + const responseBody = await response.json(); + this.assertValidResponse(request, responseBody); - const responsesById = responseEnvelope.items.reduce((acc, r) => { + const responsesById = responseBody.items.reduce((acc, r) => { acc[r.id] = r; return acc; - }, {} as Record>); + }, {} as Record>); - return requestEnvelope.items.map(request => responsesById[request.id]); + return request.items.map(query => responsesById[query.id]); } private getAuthorizationHeader(token?: string): Record { return token ? { Authorization: `Bearer ${token}` } : {}; } - private assertValidResponses( - requestEnvelope: AuthorizeRequestEnvelope, + private assertValidResponse( + request: AuthorizeRequest, json: any, - ): asserts json is AuthorizeResponseEnvelope { + ): asserts json is AuthorizeResponse { const authorizedResponses = responseSchema.parse(json); const responseIds = authorizedResponses.items.map(r => r.id); - const hasAllRequestIds = requestEnvelope.items.every(r => + const hasAllRequestIds = request.items.every(r => responseIds.includes(r.id), ); if (!hasAllRequestIds) { diff --git a/plugins/permission-common/src/types/api.ts b/plugins/permission-common/src/types/api.ts index abdae021d0..287c7ffe83 100644 --- a/plugins/permission-common/src/types/api.ts +++ b/plugins/permission-common/src/types/api.ts @@ -46,7 +46,7 @@ export enum AuthorizeResult { * An individual authorization request for {@link PermissionClient#authorize}. * @public */ -export type AuthorizeRequest = { +export type AuthorizeQuery = { permission: Permission; resourceRef?: string; }; @@ -55,8 +55,8 @@ export type AuthorizeRequest = { * A batch of authorization requests from {@link PermissionClient#authorize}. * @public */ -export type AuthorizeRequestEnvelope = { - items: Identified[]; +export type AuthorizeRequest = { + items: Identified[]; }; /** @@ -86,7 +86,7 @@ export type PermissionCriteria = * An individual authorization response from {@link PermissionClient#authorize}. * @public */ -export type AuthorizeResponse = +export type AuthorizeDecision = | { result: AuthorizeResult.ALLOW | AuthorizeResult.DENY } | { result: AuthorizeResult.CONDITIONAL; @@ -97,6 +97,6 @@ export type AuthorizeResponse = * A batch of authorization responses from {@link PermissionClient#authorize}. * @public */ -export type AuthorizeResponseEnvelope = { - items: Identified[]; +export type AuthorizeResponse = { + items: Identified[]; }; diff --git a/plugins/permission-common/src/types/index.ts b/plugins/permission-common/src/types/index.ts index 9fbe911941..b4453fcc63 100644 --- a/plugins/permission-common/src/types/index.ts +++ b/plugins/permission-common/src/types/index.ts @@ -16,10 +16,10 @@ export { AuthorizeResult } from './api'; export type { + AuthorizeQuery, AuthorizeRequest, - AuthorizeRequestEnvelope, + AuthorizeDecision, AuthorizeResponse, - AuthorizeResponseEnvelope, Identified, PermissionCondition, PermissionCriteria, diff --git a/plugins/permission-common/src/types/permission.ts b/plugins/permission-common/src/types/permission.ts index 246f1bec72..805aefa3de 100644 --- a/plugins/permission-common/src/types/permission.ts +++ b/plugins/permission-common/src/types/permission.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { AuthorizeRequest, AuthorizeResponse } from './api'; +import { AuthorizeQuery, AuthorizeDecision } from './api'; /** * The attributes related to a given permission; these should be generic and widely applicable to @@ -48,9 +48,9 @@ export type Permission = { */ export interface PermissionAuthorizer { authorize( - requests: AuthorizeRequest[], + queries: AuthorizeQuery[], options?: AuthorizeRequestOptions, - ): Promise; + ): Promise; } /** diff --git a/plugins/permission-node/api-report.md b/plugins/permission-node/api-report.md index 71685c59b1..43bb548a06 100644 --- a/plugins/permission-node/api-report.md +++ b/plugins/permission-node/api-report.md @@ -3,9 +3,9 @@ > Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). ```ts -import { AuthorizeRequest } from '@backstage/plugin-permission-common'; +import { AuthorizeDecision } from '@backstage/plugin-permission-common'; +import { AuthorizeQuery } from '@backstage/plugin-permission-common'; import { AuthorizeRequestOptions } from '@backstage/plugin-permission-common'; -import { AuthorizeResponse } from '@backstage/plugin-permission-common'; import { AuthorizeResult } from '@backstage/plugin-permission-common'; import { BackstageIdentityResponse } from '@backstage/plugin-auth-backend'; import { Config } from '@backstage/config'; @@ -129,7 +129,7 @@ export const makeCreatePermissionRule: () => < export interface PermissionPolicy { // (undocumented) handle( - request: PolicyAuthorizeRequest, + request: PolicyAuthorizeQuery, user?: BackstageIdentityResponse, ): Promise; } @@ -147,7 +147,7 @@ export type PermissionRule< }; // @public -export type PolicyAuthorizeRequest = Omit; +export type PolicyAuthorizeQuery = Omit; // @public export type PolicyDecision = @@ -158,9 +158,9 @@ export type PolicyDecision = export class ServerPermissionClient implements PermissionAuthorizer { // (undocumented) authorize( - requests: AuthorizeRequest[], + queries: AuthorizeQuery[], options?: AuthorizeRequestOptions, - ): Promise; + ): Promise; // (undocumented) static fromConfig( config: Config, diff --git a/plugins/permission-node/src/ServerPermissionClient.test.ts b/plugins/permission-node/src/ServerPermissionClient.test.ts index 4864aa4606..9fa1eadb5e 100644 --- a/plugins/permission-node/src/ServerPermissionClient.test.ts +++ b/plugins/permission-node/src/ServerPermissionClient.test.ts @@ -18,7 +18,7 @@ import { ServerPermissionClient } from './ServerPermissionClient'; import { Permission, Identified, - AuthorizeRequest, + AuthorizeQuery, AuthorizeResult, } from '@backstage/plugin-permission-common'; import { ConfigReader } from '@backstage/config'; @@ -32,7 +32,7 @@ import { RestContext, rest } from 'msw'; const server = setupServer(); const mockAuthorizeHandler = jest.fn((req, res, { json }: RestContext) => { - const responses = req.body.items.map((r: Identified) => ({ + const responses = req.body.items.map((r: Identified) => ({ id: r.id, result: AuthorizeResult.ALLOW, })); diff --git a/plugins/permission-node/src/ServerPermissionClient.ts b/plugins/permission-node/src/ServerPermissionClient.ts index c85cc7048b..f22779c8e8 100644 --- a/plugins/permission-node/src/ServerPermissionClient.ts +++ b/plugins/permission-node/src/ServerPermissionClient.ts @@ -20,9 +20,9 @@ import { } from '@backstage/backend-common'; import { Config } from '@backstage/config'; import { - AuthorizeRequest, + AuthorizeQuery, AuthorizeRequestOptions, - AuthorizeResponse, + AuthorizeDecision, AuthorizeResult, PermissionClient, PermissionAuthorizer, @@ -78,9 +78,9 @@ export class ServerPermissionClient implements PermissionAuthorizer { } async authorize( - requests: AuthorizeRequest[], + queries: AuthorizeQuery[], options?: AuthorizeRequestOptions, - ): Promise { + ): Promise { // Check if permissions are enabled before validating the server token. That // way when permissions are disabled, the noop token manager can be used // without fouling up the logic inside the ServerPermissionClient, because @@ -89,9 +89,9 @@ export class ServerPermissionClient implements PermissionAuthorizer { !this.permissionEnabled || (await this.isValidServerToken(options?.token)) ) { - return requests.map(_ => ({ result: AuthorizeResult.ALLOW })); + return queries.map(_ => ({ result: AuthorizeResult.ALLOW })); } - return this.permissionClient.authorize(requests, options); + return this.permissionClient.authorize(queries, options); } private async isValidServerToken( diff --git a/plugins/permission-node/src/policy/index.ts b/plugins/permission-node/src/policy/index.ts index 1b05f240d3..1d3fc4a737 100644 --- a/plugins/permission-node/src/policy/index.ts +++ b/plugins/permission-node/src/policy/index.ts @@ -18,6 +18,6 @@ export type { ConditionalPolicyDecision, DefinitivePolicyDecision, PermissionPolicy, - PolicyAuthorizeRequest, + PolicyAuthorizeQuery, PolicyDecision, } from './types'; diff --git a/plugins/permission-node/src/policy/types.ts b/plugins/permission-node/src/policy/types.ts index 4c6a033e11..2e344d6d96 100644 --- a/plugins/permission-node/src/policy/types.ts +++ b/plugins/permission-node/src/policy/types.ts @@ -15,7 +15,7 @@ */ import { - AuthorizeRequest, + AuthorizeQuery, AuthorizeResult, PermissionCondition, PermissionCriteria, @@ -27,13 +27,13 @@ import { BackstageIdentityResponse } from '@backstage/plugin-auth-backend'; * * @remarks * - * This differs from {@link @backstage/permission-common#AuthorizeRequest} in that `resourceRef` + * This differs from {@link @backstage/permission-common#AuthorizeQuery} in that `resourceRef` * should never be provided. This forces policies to be written in a way that's compatible with * filtering collections of resources at data load time. * * @public */ -export type PolicyAuthorizeRequest = Omit; +export type PolicyAuthorizeQuery = Omit; /** * A definitive result to an authorization request, returned by the {@link PermissionPolicy}. @@ -57,7 +57,7 @@ export type DefinitivePolicyDecision = { * conditions hold when evaluated. The conditions will be evaluated by the corresponding plugin * which knows about the referenced permission rules. * - * Similar to {@link @backstage/permission-common#AuthorizeResult}, but with the plugin and resource + * Similar to {@link @backstage/permission-common#AuthorizeDecision}, but with the plugin and resource * identifiers needed to evaluate the returned conditions. * @public */ @@ -95,7 +95,7 @@ export type PolicyDecision = */ export interface PermissionPolicy { handle( - request: PolicyAuthorizeRequest, + request: PolicyAuthorizeQuery, user?: BackstageIdentityResponse, ): Promise; } diff --git a/plugins/permission-node/src/types.ts b/plugins/permission-node/src/types.ts index 678befc99c..d5035f10dc 100644 --- a/plugins/permission-node/src/types.ts +++ b/plugins/permission-node/src/types.ts @@ -18,7 +18,7 @@ import type { PermissionCriteria } from '@backstage/plugin-permission-common'; /** * A conditional rule that can be provided in an - * {@link @backstage/permission-common#AuthorizeResult} response to an authorization request. + * {@link @backstage/permission-common#AuthorizeDecision} response to an authorization request. * * @remarks * diff --git a/plugins/permission-react/api-report.md b/plugins/permission-react/api-report.md index 799b3ad54f..1bcb01fa32 100644 --- a/plugins/permission-react/api-report.md +++ b/plugins/permission-react/api-report.md @@ -4,8 +4,8 @@ ```ts import { ApiRef } from '@backstage/core-plugin-api'; -import { AuthorizeRequest } from '@backstage/plugin-permission-common'; -import { AuthorizeResponse } from '@backstage/plugin-permission-common'; +import { AuthorizeDecision } from '@backstage/plugin-permission-common'; +import { AuthorizeQuery } from '@backstage/plugin-permission-common'; import { ComponentProps } from 'react'; import { Config } from '@backstage/config'; import { DiscoveryApi } from '@backstage/core-plugin-api'; @@ -24,7 +24,7 @@ export type AsyncPermissionResult = { // @public export class IdentityPermissionApi implements PermissionApi { // (undocumented) - authorize(request: AuthorizeRequest): Promise; + authorize(request: AuthorizeQuery): Promise; // (undocumented) static create(options: { config: Config; @@ -35,7 +35,7 @@ export class IdentityPermissionApi implements PermissionApi { // @public export type PermissionApi = { - authorize(request: AuthorizeRequest): Promise; + authorize(request: AuthorizeQuery): Promise; }; // @public diff --git a/plugins/permission-react/src/apis/IdentityPermissionApi.ts b/plugins/permission-react/src/apis/IdentityPermissionApi.ts index 818eca17fa..7d126c56cb 100644 --- a/plugins/permission-react/src/apis/IdentityPermissionApi.ts +++ b/plugins/permission-react/src/apis/IdentityPermissionApi.ts @@ -17,8 +17,8 @@ import { DiscoveryApi, IdentityApi } from '@backstage/core-plugin-api'; import { PermissionApi } from './PermissionApi'; import { - AuthorizeRequest, - AuthorizeResponse, + AuthorizeQuery, + AuthorizeDecision, PermissionClient, } from '@backstage/plugin-permission-common'; import { Config } from '@backstage/config'; @@ -44,7 +44,7 @@ export class IdentityPermissionApi implements PermissionApi { return new IdentityPermissionApi(permissionClient, identity); } - async authorize(request: AuthorizeRequest): Promise { + async authorize(request: AuthorizeQuery): Promise { const response = await this.permissionClient.authorize([request], { token: await this.identityApi.getIdToken(), }); diff --git a/plugins/permission-react/src/apis/PermissionApi.ts b/plugins/permission-react/src/apis/PermissionApi.ts index 5c224ff06d..b17350c38e 100644 --- a/plugins/permission-react/src/apis/PermissionApi.ts +++ b/plugins/permission-react/src/apis/PermissionApi.ts @@ -15,8 +15,8 @@ */ import { - AuthorizeRequest, - AuthorizeResponse, + AuthorizeQuery, + AuthorizeDecision, } from '@backstage/plugin-permission-common'; import { ApiRef, createApiRef } from '@backstage/core-plugin-api'; @@ -27,7 +27,7 @@ import { ApiRef, createApiRef } from '@backstage/core-plugin-api'; * @public */ export type PermissionApi = { - authorize(request: AuthorizeRequest): Promise; + authorize(request: AuthorizeQuery): Promise; }; /** From 12f5c330430ae5d9e8de26856aeee23bf3135f71 Mon Sep 17 00:00:00 2001 From: MT Lewis Date: Thu, 13 Jan 2022 13:30:08 +0000 Subject: [PATCH 077/116] catalog-backend: rename permission-related variable to match type Signed-off-by: MT Lewis --- .../src/service/AuthorizedEntitiesCatalog.ts | 8 ++++---- .../src/service/AuthorizedRefreshService.ts | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts index 5b65070c43..270eb64a40 100644 --- a/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/AuthorizedEntitiesCatalog.ts @@ -36,23 +36,23 @@ export class AuthorizedEntitiesCatalog implements EntitiesCatalog { ) {} async entities(request?: EntitiesRequest): Promise { - const authorizeResponse = ( + const authorizeDecision = ( await this.permissionApi.authorize( [{ permission: catalogEntityReadPermission }], { token: request?.authorizationToken }, ) )[0]; - if (authorizeResponse.result === AuthorizeResult.DENY) { + if (authorizeDecision.result === AuthorizeResult.DENY) { return { entities: [], pageInfo: { hasNextPage: false }, }; } - if (authorizeResponse.result === AuthorizeResult.CONDITIONAL) { + if (authorizeDecision.result === AuthorizeResult.CONDITIONAL) { const permissionFilter: EntityFilter = this.transformConditions( - authorizeResponse.conditions, + authorizeDecision.conditions, ); return this.entitiesCatalog.entities({ ...request, diff --git a/plugins/catalog-backend/src/service/AuthorizedRefreshService.ts b/plugins/catalog-backend/src/service/AuthorizedRefreshService.ts index 800bdcf1b9..819451d854 100644 --- a/plugins/catalog-backend/src/service/AuthorizedRefreshService.ts +++ b/plugins/catalog-backend/src/service/AuthorizedRefreshService.ts @@ -28,7 +28,7 @@ export class AuthorizedRefreshService implements RefreshService { ) {} async refresh(options: RefreshOptions) { - const authorizeResponse = ( + const authorizeDecision = ( await this.permissionApi.authorize( [ { @@ -39,7 +39,7 @@ export class AuthorizedRefreshService implements RefreshService { { token: options.authorizationToken }, ) )[0]; - if (authorizeResponse.result !== AuthorizeResult.ALLOW) { + if (authorizeDecision.result !== AuthorizeResult.ALLOW) { throw new NotAllowedError(); } await this.service.refresh(options); From a4487f70509dc73cad6d4787099039b780f05e7f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Jan 2022 04:10:17 +0000 Subject: [PATCH 078/116] build(deps): bump @roadiehq/backstage-plugin-github-insights Bumps [@roadiehq/backstage-plugin-github-insights](https://github.com/RoadieHQ/roadie-backstage-plugins/tree/HEAD/plugins/frontend/backstage-plugin-github-insights) from 1.4.2 to 1.4.3. - [Release notes](https://github.com/RoadieHQ/roadie-backstage-plugins/releases) - [Changelog](https://github.com/RoadieHQ/roadie-backstage-plugins/blob/main/plugins/frontend/backstage-plugin-github-insights/CHANGELOG.md) - [Commits](https://github.com/RoadieHQ/roadie-backstage-plugins/commits/HEAD/plugins/frontend/backstage-plugin-github-insights) --- updated-dependencies: - dependency-name: "@roadiehq/backstage-plugin-github-insights" dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- yarn.lock | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/yarn.lock b/yarn.lock index c8c2cc5530..b995fc1993 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5377,12 +5377,11 @@ integrity sha512-8UiDeDbjCImFSfOegGu13otQ7OdP9FOYpcLjeouppnhs+MPeIEAtYS+jCcBKmi3reyTagC15/KVSRhde1wS1vg== "@roadiehq/backstage-plugin-github-insights@^1.4.2": - version "1.4.2" - resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-1.4.2.tgz#462e2868a1b4cee338032e4715ad797db71ebd22" - integrity sha512-hIGOhzyK1rC3JI+y/yI303whdRJTN4KXCYHekcdchjlTKB31SMU4lER0sBacCyiov1vAl+LkDmdubESSLou/Hw== + version "1.4.3" + resolved "https://registry.npmjs.org/@roadiehq/backstage-plugin-github-insights/-/backstage-plugin-github-insights-1.4.3.tgz#c83fd94dc7266330b464686c5f283fa07f5f2be9" + integrity sha512-khvXVgp+GTwXULTG4jfzB6uQtzPbjgZhfudE04hc/O3FWU4Wyrcmc/UTTu1nFah7m6yL6pwfEdC3auKXPr07lQ== dependencies: "@backstage/catalog-model" "^0.9.7" - "@backstage/core-app-api" "^0.3.0" "@backstage/core-components" "^0.8.0" "@backstage/core-plugin-api" "^0.4.0" "@backstage/integration-react" "^0.1.10" From ced88f1b08100de780892b16bacba2b81bfee310 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 13 Jan 2022 23:24:21 +0100 Subject: [PATCH 079/116] chore: steps are required Signed-off-by: blam --- docs/features/software-catalog/descriptor-format.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index ec4e13c1d4..a38910df6c 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -727,7 +727,7 @@ filtering templates, and should ideally match the Component You can find out more about the `parameters` key [here](../software-templates/writing-templates.md) -### `spec.steps` [optional] +### `spec.steps` [required] You can find out more about the `steps` key [here](../software-templates/writing-templates.md) From 2f89895af63f745f5121eed5b726f16d7da1d6ba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Jan 2022 04:09:48 +0000 Subject: [PATCH 080/116] build(deps): bump @svgr/plugin-svgo from 5.4.0 to 6.2.0 Bumps [@svgr/plugin-svgo](https://github.com/gregberge/svgr) from 5.4.0 to 6.2.0. - [Release notes](https://github.com/gregberge/svgr/releases) - [Changelog](https://github.com/gregberge/svgr/blob/main/CHANGELOG.md) - [Commits](https://github.com/gregberge/svgr/compare/v5.4.0...v6.2.0) --- updated-dependencies: - dependency-name: "@svgr/plugin-svgo" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- packages/cli/package.json | 2 +- yarn.lock | 93 ++++++--------------------------------- 2 files changed, 14 insertions(+), 81 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index edb2cfb3ce..4df006244b 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -46,7 +46,7 @@ "@sucrase/jest-plugin": "^2.1.1", "@sucrase/webpack-loader": "^2.0.0", "@svgr/plugin-jsx": "5.5.x", - "@svgr/plugin-svgo": "5.4.x", + "@svgr/plugin-svgo": "6.2.x", "@svgr/rollup": "5.5.x", "@svgr/webpack": "5.5.x", "@types/webpack-env": "^1.15.2", diff --git a/yarn.lock b/yarn.lock index 8f8c08a0e1..adcbbf417c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6820,14 +6820,14 @@ "@svgr/hast-util-to-babel-ast" "^5.5.0" svg-parser "^2.0.2" -"@svgr/plugin-svgo@5.4.x": - version "5.4.0" - resolved "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-5.4.0.tgz#45d9800b7099a6f7b4d85ebac89ab9abe8592f64" - integrity sha512-3Cgv3aYi1l6SHyzArV9C36yo4kgwVdF3zPQUC6/aCDUeXAofDYwE5kk3e3oT5ZO2a0N3lB+lLGvipBG6lnG8EA== +"@svgr/plugin-svgo@6.2.x": + version "6.2.0" + resolved "https://registry.npmjs.org/@svgr/plugin-svgo/-/plugin-svgo-6.2.0.tgz#4cbe6a33ccccdcae4e3b63ded64cc1cbe1faf48c" + integrity sha512-oDdMQONKOJEbuKwuy4Np6VdV6qoaLLvoY86hjvQEgU82Vx1MSWRyYms6Sl0f+NtqxLI/rDVufATbP/ev996k3Q== dependencies: - cosmiconfig "^6.0.0" - merge-deep "^3.0.2" - svgo "^1.2.2" + cosmiconfig "^7.0.1" + deepmerge "^4.2.2" + svgo "^2.5.0" "@svgr/plugin-svgo@^5.5.0": version "5.5.0" @@ -11261,17 +11261,6 @@ clone-buffer@^1.0.0: resolved "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= -clone-deep@^0.2.4: - version "0.2.4" - resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" - integrity sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY= - dependencies: - for-own "^0.1.3" - is-plain-object "^2.0.1" - kind-of "^3.0.2" - lazy-cache "^1.0.3" - shallow-clone "^0.1.2" - clone-deep@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" @@ -11949,7 +11938,7 @@ cosmiconfig-toml-loader@1.0.0: dependencies: "@iarna/toml" "^2.2.5" -cosmiconfig@7.0.0, cosmiconfig@^7.0.0: +cosmiconfig@7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== @@ -11960,7 +11949,7 @@ cosmiconfig@7.0.0, cosmiconfig@^7.0.0: path-type "^4.0.0" yaml "^1.10.0" -cosmiconfig@7.0.1: +cosmiconfig@7.0.1, cosmiconfig@^7.0.0, cosmiconfig@^7.0.1: version "7.0.1" resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.1.tgz#714d756522cace867867ccb4474c5d01bbae5d6d" integrity sha512-a1YWNUV2HwGimB7dU2s1wUMurNKjpx60HxBB6xUM8Re+2s1g1IIfJvFR0/iCF+XHdE0GMTKTuLR32UQff4TEyQ== @@ -14999,23 +14988,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0: resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.7.tgz#2004c02eb9436eee9a21446a6477debf17e81685" integrity sha512-+hbxoLbFMbRKDwohX8GkTataGqO6Jb7jGwpAlwgy2bIz25XtRm7KEzJM76R1WiNT5SwZkX4Y75SwBolkpmE7iQ== -for-in@^0.1.3: - version "0.1.8" - resolved "https://registry.npmjs.org/for-in/-/for-in-0.1.8.tgz#d8773908e31256109952b1fdb9b3fa867d2775e1" - integrity sha1-2Hc5COMSVhCZUrH9ubP6hn0ndeE= - -for-in@^1.0.1, for-in@^1.0.2: +for-in@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= -for-own@^0.1.3: - version "0.1.5" - resolved "https://registry.npmjs.org/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" - integrity sha1-UmXGgaTylNq78XyVCbZ2OqhFEM4= - dependencies: - for-in "^1.0.1" - foreach@^2.0.4, foreach@^2.0.5: version "2.0.5" resolved "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" @@ -17071,7 +17048,7 @@ is-boolean-object@^1.1.0: dependencies: call-bind "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -17413,7 +17390,7 @@ is-plain-obj@^4.0.0: resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.0.0.tgz#06c0999fd7574edf5a906ba5644ad0feb3a84d22" integrity sha512-NXRbBtUdBioI73y/HmOhogw/U5msYPC9DAtGkJXeFcFWSFZw0mCUsPxk/snTuJHzNKA8kLBK4rH97RMB1BfCXw== -is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: +is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== @@ -18854,13 +18831,6 @@ keyv@^4.0.0, keyv@^4.0.3: dependencies: json-buffer "3.0.1" -kind-of@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz#018ec7a4ce7e3a86cb9141be519d24c8faa981b5" - integrity sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU= - dependencies: - is-buffer "^1.0.2" - kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: version "3.2.2" resolved "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" @@ -18950,16 +18920,6 @@ lazy-ass@1.6.0, lazy-ass@^1.6.0: resolved "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz#7999655e8646c17f089fdd187d150d3324d54513" integrity sha1-eZllXoZGwX8In90YfRUNMyTVRRM= -lazy-cache@^0.2.3: - version "0.2.7" - resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-0.2.7.tgz#7feddf2dcb6edb77d11ef1d117ab5ffdf0ab1b65" - integrity sha1-f+3fLctu23fRHvHRF6tf/fCrG2U= - -lazy-cache@^1.0.3: - version "1.0.4" - resolved "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" - integrity sha1-odePw6UEdMuAhF07O24dpJpEbo4= - lazy-universal-dotenv@^3.0.1: version "3.0.1" resolved "https://registry.npmjs.org/lazy-universal-dotenv/-/lazy-universal-dotenv-3.0.1.tgz#a6c8938414bca426ab8c9463940da451a911db38" @@ -20146,15 +20106,6 @@ meow@^8.0.0: type-fest "^0.18.0" yargs-parser "^20.2.3" -merge-deep@^3.0.2: - version "3.0.3" - resolved "https://registry.npmjs.org/merge-deep/-/merge-deep-3.0.3.tgz#1a2b2ae926da8b2ae93a0ac15d90cd1922766003" - integrity sha512-qtmzAS6t6grwEkNrunqTBdn0qKwFgNWvlxUbAV8es9M7Ot1EbyApytCnvE0jALPa46ZpKDUo527kKiaWplmlFA== - dependencies: - arr-union "^3.1.0" - clone-deep "^0.2.4" - kind-of "^3.0.2" - merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -20697,14 +20648,6 @@ mixin-deep@^1.2.0: for-in "^1.0.2" is-extendable "^1.0.1" -mixin-object@^2.0.1: - version "2.0.1" - resolved "https://registry.npmjs.org/mixin-object/-/mixin-object-2.0.1.tgz#4fb949441dab182540f1fe035ba60e1947a5e57e" - integrity sha1-T7lJRB2rGCVA8f4DW6YOGUel5X4= - dependencies: - for-in "^0.1.3" - is-extendable "^0.1.1" - mixme@^0.5.1: version "0.5.4" resolved "https://registry.npmjs.org/mixme/-/mixme-0.5.4.tgz#8cb3bd0cd32a513c161bf1ca99d143f0bcf2eff3" @@ -25501,16 +25444,6 @@ sha.js@^2.4.0, sha.js@^2.4.11, sha.js@^2.4.8, sha.js@^2.4.9: inherits "^2.0.1" safe-buffer "^5.0.1" -shallow-clone@^0.1.2: - version "0.1.2" - resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz#5909e874ba77106d73ac414cfec1ffca87d97060" - integrity sha1-WQnodLp3EG1zrEFM/sH/yofZcGA= - dependencies: - is-extendable "^0.1.1" - kind-of "^2.0.1" - lazy-cache "^0.2.3" - mixin-object "^2.0.1" - shallow-clone@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" @@ -26671,7 +26604,7 @@ svgo@^1.2.2: unquote "~1.1.1" util.promisify "~1.0.0" -svgo@^2.7.0: +svgo@^2.5.0, svgo@^2.7.0: version "2.8.0" resolved "https://registry.npmjs.org/svgo/-/svgo-2.8.0.tgz#4ff80cce6710dc2795f0c7c74101e6764cfccd24" integrity sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg== From 05a777a46c7f9f4d76ab93cc84fad6866536c40a Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 14 Jan 2022 09:54:57 +0100 Subject: [PATCH 081/116] changeset: enter prerelease mode Signed-off-by: Patrik Oldsberg --- .changeset/pre.json | 128 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 .changeset/pre.json diff --git a/.changeset/pre.json b/.changeset/pre.json new file mode 100644 index 0000000000..5f0d5e837d --- /dev/null +++ b/.changeset/pre.json @@ -0,0 +1,128 @@ +{ + "mode": "pre", + "tag": "next", + "initialVersions": { + "example-app": "0.2.60", + "@backstage/app-defaults": "0.1.4", + "example-backend": "0.2.60", + "@backstage/backend-common": "0.10.3", + "@backstage/backend-tasks": "0.1.3", + "@backstage/backend-test-utils": "0.1.13", + "@backstage/catalog-client": "0.5.4", + "@backstage/catalog-model": "0.9.9", + "@backstage/cli": "0.11.0", + "@backstage/cli-common": "0.1.6", + "@backstage/codemods": "0.1.29", + "@backstage/config": "0.1.12", + "@backstage/config-loader": "0.9.2", + "@backstage/core-app-api": "0.4.0", + "@backstage/core-components": "0.8.4", + "@backstage/core-plugin-api": "0.5.0", + "@backstage/create-app": "0.4.12", + "@backstage/dev-utils": "0.2.17", + "e2e-test": "0.2.0", + "embedded-techdocs-app": "0.2.59", + "@backstage/errors": "0.2.0", + "@backstage/integration": "0.7.1", + "@backstage/integration-react": "0.1.18", + "@backstage/search-common": "0.2.1", + "storybook": "0.2.1", + "@techdocs/cli": "0.8.10", + "@backstage/techdocs-common": "0.11.3", + "@backstage/test-utils": "0.2.2", + "@backstage/theme": "0.2.14", + "@backstage/types": "0.1.1", + "@backstage/version-bridge": "0.1.1", + "@backstage/plugin-airbrake": "0.1.0", + "@backstage/plugin-allure": "0.1.11", + "@backstage/plugin-analytics-module-ga": "0.1.6", + "@backstage/plugin-apache-airflow": "0.1.3", + "@backstage/plugin-api-docs": "0.6.22", + "@backstage/plugin-app-backend": "0.3.21", + "@backstage/plugin-auth-backend": "0.6.2", + "@backstage/plugin-azure-devops": "0.1.10", + "@backstage/plugin-azure-devops-backend": "0.3.0", + "@backstage/plugin-azure-devops-common": "0.2.0", + "@backstage/plugin-badges": "0.2.19", + "@backstage/plugin-badges-backend": "0.1.15", + "@backstage/plugin-bazaar": "0.1.9", + "@backstage/plugin-bazaar-backend": "0.1.6", + "@backstage/plugin-bitrise": "0.1.22", + "@backstage/plugin-catalog": "0.7.8", + "@backstage/plugin-catalog-backend": "0.20.0", + "@backstage/plugin-catalog-backend-module-ldap": "0.3.9", + "@backstage/plugin-catalog-backend-module-msgraph": "0.2.12", + "@backstage/plugin-catalog-common": "0.1.0", + "@backstage/plugin-catalog-graph": "0.2.6", + "@backstage/plugin-catalog-graphql": "0.3.0", + "@backstage/plugin-catalog-import": "0.7.9", + "@backstage/plugin-catalog-react": "0.6.11", + "@backstage/plugin-circleci": "0.2.34", + "@backstage/plugin-cloudbuild": "0.2.32", + "@backstage/plugin-code-coverage": "0.1.22", + "@backstage/plugin-code-coverage-backend": "0.1.19", + "@backstage/plugin-config-schema": "0.1.18", + "@backstage/plugin-cost-insights": "0.11.17", + "@backstage/plugin-explore": "0.3.25", + "@backstage/plugin-explore-react": "0.0.10", + "@backstage/plugin-firehydrant": "0.1.12", + "@backstage/plugin-fossa": "0.2.27", + "@backstage/plugin-gcp-projects": "0.3.13", + "@backstage/plugin-git-release-manager": "0.3.8", + "@backstage/plugin-github-actions": "0.4.31", + "@backstage/plugin-github-deployments": "0.1.26", + "@backstage/plugin-gitops-profiles": "0.3.13", + "@backstage/plugin-gocd": "0.1.1", + "@backstage/plugin-graphiql": "0.2.27", + "@backstage/plugin-graphql-backend": "0.1.11", + "@backstage/plugin-home": "0.4.10", + "@backstage/plugin-ilert": "0.1.21", + "@backstage/plugin-jenkins": "0.5.17", + "@backstage/plugin-jenkins-backend": "0.1.10", + "@backstage/plugin-kafka": "0.2.25", + "@backstage/plugin-kafka-backend": "0.2.14", + "@backstage/plugin-kubernetes": "0.5.4", + "@backstage/plugin-kubernetes-backend": "0.4.3", + "@backstage/plugin-kubernetes-common": "0.2.1", + "@backstage/plugin-lighthouse": "0.2.34", + "@backstage/plugin-newrelic": "0.3.13", + "@backstage/plugin-newrelic-dashboard": "0.1.3", + "@backstage/plugin-org": "0.3.34", + "@backstage/plugin-pagerduty": "0.3.22", + "@backstage/plugin-permission-backend": "0.3.0", + "@backstage/plugin-permission-common": "0.3.1", + "@backstage/plugin-permission-node": "0.3.0", + "@backstage/plugin-permission-react": "0.2.2", + "@backstage/plugin-proxy-backend": "0.2.15", + "@backstage/plugin-rollbar": "0.3.23", + "@backstage/plugin-rollbar-backend": "0.1.18", + "@backstage/plugin-scaffolder": "0.11.18", + "@backstage/plugin-scaffolder-backend": "0.15.20", + "@backstage/plugin-scaffolder-backend-module-cookiecutter": "0.1.8", + "@backstage/plugin-scaffolder-backend-module-rails": "0.2.3", + "@backstage/plugin-scaffolder-backend-module-yeoman": "0.1.2", + "@backstage/plugin-scaffolder-common": "0.1.2", + "@backstage/plugin-search": "0.5.5", + "@backstage/plugin-search-backend": "0.3.0", + "@backstage/plugin-search-backend-module-elasticsearch": "0.0.7", + "@backstage/plugin-search-backend-module-pg": "0.2.3", + "@backstage/plugin-search-backend-node": "0.4.4", + "@backstage/plugin-sentry": "0.3.33", + "@backstage/plugin-shortcuts": "0.1.19", + "@backstage/plugin-sonarqube": "0.2.12", + "@backstage/plugin-splunk-on-call": "0.3.19", + "@backstage/plugin-tech-insights": "0.1.5", + "@backstage/plugin-tech-insights-backend": "0.1.5", + "@backstage/plugin-tech-insights-backend-module-jsonfc": "0.1.5", + "@backstage/plugin-tech-insights-common": "0.2.1", + "@backstage/plugin-tech-insights-node": "0.1.2", + "@backstage/plugin-tech-radar": "0.5.2", + "@backstage/plugin-techdocs": "0.12.14", + "@backstage/plugin-techdocs-backend": "0.12.3", + "@backstage/plugin-todo": "0.1.18", + "@backstage/plugin-todo-backend": "0.1.18", + "@backstage/plugin-user-settings": "0.3.16", + "@backstage/plugin-xcmetrics": "0.2.15" + }, + "changesets": [] +} From ee45cd42d84246d00e01b3ac4ca694684a646041 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:15:45 +0100 Subject: [PATCH 082/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index ad5e78bedb..c35c4d9b3f 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -63,7 +63,7 @@ cycle will vary based on maintainer schedules. ### Backstage 1.0 (and following versions) -During the first quarter of 2022 we plan to finalize and release +During the first quarter of 2022, we plan to finalize and release version 1.0 of the Backstage platform (defined by the Core, [Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), [Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) From 796454994ba00b26db817989607051ce2a6f4edd Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:15:57 +0100 Subject: [PATCH 083/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index c35c4d9b3f..3b96111df3 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -78,7 +78,7 @@ Included as part of this milestone: ### Backstage Security Audit This initiative is the first of a broader Security Strategy for Backstage. The -purpose of the Security Audit is to involve third party companies in auditing +purpose of the Security Audit is to involve third-party companies in auditing the platform and highlighting potential vulnerabilities (if there are any). The benefit for the adopters is clear: we want Backstage to be as secure as possible and we want to make it reliable through a specific initiative. This From 4eff1188c0ec8fcab11f493aed97d9dd7488e517 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:16:32 +0100 Subject: [PATCH 084/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 3b96111df3..da1ff49bc9 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -79,7 +79,7 @@ Included as part of this milestone: This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third-party companies in auditing -the platform and highlighting potential vulnerabilities (if there are any). The +the platform and highlighting potential vulnerabilities. The benefit for the adopters is clear: we want Backstage to be as secure as possible and we want to make it reliable through a specific initiative. This initiative in particular is done together (and with the support of) the From 06860ac82826f46d52549d925ef37c0440ab8fcf Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:16:39 +0100 Subject: [PATCH 085/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index da1ff49bc9..ce80125a19 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -80,7 +80,7 @@ Included as part of this milestone: This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third-party companies in auditing the platform and highlighting potential vulnerabilities. The -benefit for the adopters is clear: we want Backstage to be as secure as +benefit for the adopters is clear: We want Backstage to be as secure as possible and we want to make it reliable through a specific initiative. This initiative in particular is done together (and with the support of) the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). From 5ed2ad9648edde6e6ada6e070f073a86f356f506 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:16:47 +0100 Subject: [PATCH 086/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index ce80125a19..6b54d3c13d 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -81,7 +81,7 @@ This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third-party companies in auditing the platform and highlighting potential vulnerabilities. The benefit for the adopters is clear: We want Backstage to be as secure as -possible and we want to make it reliable through a specific initiative. This +possible, and we want to make it reliable through a specific initiative. This initiative in particular is done together (and with the support of) the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). From af06098589fa89984bb2483fd5745c967553300a Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:16:59 +0100 Subject: [PATCH 087/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 6b54d3c13d..1125ae15ff 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -82,7 +82,7 @@ purpose of the Security Audit is to involve third-party companies in auditing the platform and highlighting potential vulnerabilities. The benefit for the adopters is clear: We want Backstage to be as secure as possible, and we want to make it reliable through a specific initiative. This -initiative in particular is done together (and with the support of) the +initiative in particular is done together, and with the support of, the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). ### Moving to Incubation in CNCF From cf77b60e0e6f27e94e4070a69766a1d6d096f117 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 10:17:07 +0100 Subject: [PATCH 088/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 1125ae15ff..aca775da82 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -92,7 +92,7 @@ The progress of the request can be seen ## Future work -The following feature list doesn’t represent a commitment to develop and the +The following feature list doesn’t represent a commitment to develop, and the list order doesn’t reflect any priority or importance. But these features are on the maintainers’ radar, with clear interest expressed by the community. From 7ea8ab0c331c643044dabb63858e8942a1ab6706 Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Fri, 14 Jan 2022 11:27:55 +0100 Subject: [PATCH 089/116] docs: remove composability docs for how to port legacy apps Signed-off-by: Patrik Oldsberg --- docs/plugins/composability.md | 154 ---------------------------------- 1 file changed, 154 deletions(-) diff --git a/docs/plugins/composability.md b/docs/plugins/composability.md index 05af512f92..de4bc15cf1 100644 --- a/docs/plugins/composability.md +++ b/docs/plugins/composability.md @@ -522,157 +522,3 @@ clarify intent. Refer to the following table to formulate the new name: | Entity Overview Card | \*Card | Entity\*Card | EntitySentryCard, EntityPagerDutyCard | | Entity Conditional | isPluginApplicableToEntity | is\*Available | isPagerDutyAvailable, isJenkinsAvailable | | Plugin Instance | plugin | \*Plugin | jenkinsPlugin, catalogPlugin | - -## Porting Existing Apps - -The first step of porting any app is to replace the root `Routes` component with -`FlatRoutes` from `@backstage/core-app-api`. As opposed to the `Routes` -component, `FlatRoutes` only considers the first level of `Route` components in -its children, and provides any additional children to the outlet of the route. -It also removes the need to append `"/*"` to paths, as it is added -automatically. - -```diff -const AppRoutes = () => ( -- -+ - ... -- } /> -+ } /> - ... -- -+ -); -``` - -The next step should be to switch from using `EntityPageLayout` to -`EntityLayout`, as this can also be done without waiting for plugins to be -ported. You should also replace the top-level `Router` from the catalog plugin -with the separate `CatalogIndexPage` and `CatalogEntityPage` extensions that -have been added to the catalog: - -```diff --} --/> -+} /> -+} -+> -+ -+ -``` - -At that point you should flatten out the element tree as much as possible in the -app, removing any intermediate components. At the top level this should usually -be straightforward, but when reaching the catalog entity pages you may need to -wait for some plugins to be migrated. This is because it is no longer possible -to pass in the selected entity through component props, and it should be picked -up from context inside the plugin instead. See the sections below for how to -carry out migrations of some common entity page patterns. - -Once the app element tree doesn't contain any intermediate components, and all -plugin imports have been switched to extensions rather than plain components, -the app has been fully ported. - -### Switching from EntityPageLayout to EntityLayout - -The existing `EntityPageLayout` is replaced by the new `EntityLayout` component, -which has a slightly different pattern for expressing the contents and paths. - -Porting from the old to the new API is just a matter of moving some things -around. For example, given the following existing code: - -```tsx - - } - /> - } - /> - } - /> - -``` - -It would be ported to this: - -```tsx - - - - - - - - - - - - - -``` - -In addition to the renaming, the `element` prop has been moved to `children`. -Also note that the `/*` suffix has been removed from the `"/kubernetes"` path, -as it's now added automatically. - -Usage of the `EntityLayout` component is required to be able to properly -discover routes, and so it is required to apply this change before you can start -using routable entity content extensions from plugins. - -### Porting Entity Pages - -The established pattern in the app is to use custom components in order to -select what plugin components to render for a given entity. The new -`EntitySwitch` component introduced above is what is intended to replace this -pattern, now that the entire app needs to be rendered as a single element tree. -For example, given the following existing code: - -```tsx -export const EntityPage = () => { - const { entity } = useEntity(); - - switch (entity?.kind?.toLowerCase()) { - case 'component': - return ; - case 'api': - return ; - case 'group': - return ; - case 'user': - return ; - default: - return ; - } -}; -``` - -It would be migrated to this: - -```tsx -export const entityPage = ( - - - - - - - -); -``` - -Note that for example `` has been changed to simply -`componentPage`, that is because just like the `EntityPage` component, the -`ComponentEntityPage` also needs to be ported to be an element rather a -component in a similar way. From 5f0be90d88fbcf272d87670d4718903b921bc886 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 11:47:13 +0100 Subject: [PATCH 090/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index aca775da82..ebfe231a09 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -93,7 +93,7 @@ The progress of the request can be seen ## Future work The following feature list doesn’t represent a commitment to develop, and the -list order doesn’t reflect any priority or importance. But these features are on +list order doesn’t reflect any priority or importance, but these features are on the maintainers’ radar, with clear interest expressed by the community. - **Backend Services:** To better scale and maintain the Backstage instances, a From 22052dc872271e1796d972f06ccea9a3190782f0 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 11:47:26 +0100 Subject: [PATCH 091/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index ebfe231a09..50e954cace 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -99,7 +99,7 @@ the maintainers’ radar, with clear interest expressed by the community. - **Backend Services:** To better scale and maintain the Backstage instances, a backend layer of services is planned to be introduced as part of the software architecture. This layer of backend services will help in decoupling the - various modules (Catalog and Scaffolder just to quote some) from the front-end + various modules (e.g. Catalog and Scaffolder) from the frontend experience. - **Security Plan (and Strategy):** The purpose of the Security Strategy is to move another step ahead along the path of the maturity of the platform, From 6c16957065ee848854ddde6a87ba55e292d8165b Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 11:47:35 +0100 Subject: [PATCH 092/116] Update docs/overview/roadmap.md Review from rodmachen. Co-authored-by: Rod Machen --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 50e954cace..2dedad5de7 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -102,7 +102,7 @@ the maintainers’ radar, with clear interest expressed by the community. various modules (e.g. Catalog and Scaffolder) from the frontend experience. - **Security Plan (and Strategy):** The purpose of the Security Strategy is to - move another step ahead along the path of the maturity of the platform, + move another step along the path to maturing the platform, setting the expectations of any adopters from a security standpoint. - **Search GA:**. - **[GraphQL](https://graphql.org/) support:** Introduce the ability to query From ab61c4603a230c99632af4524ebc0f15d1739d35 Mon Sep 17 00:00:00 2001 From: Francesco Corti Date: Fri, 14 Jan 2022 12:30:14 +0100 Subject: [PATCH 093/116] Updated roadmap page in the public documentation. Signed-off-by: Francesco Corti --- docs/overview/roadmap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 2dedad5de7..2eeaa00769 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -20,7 +20,7 @@ to keep us aligned as a community on: ### How to influence the roadmap As we evolve Backstage, we want you to contribute actively in the journey to -define the most effective developer experience in the world. +define the most effective developer experience in the world. A roadmap is only useful if it captures real needs. If you have success stories, feedback, or ideas, we want to hear from you! If you plan to work (or are From c8e0f906a137200650e55814c76055fb7df426e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 14 Jan 2022 13:19:11 +0100 Subject: [PATCH 094/116] prettier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/overview/roadmap.md | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/docs/overview/roadmap.md b/docs/overview/roadmap.md index 2eeaa00769..30338a6ee6 100644 --- a/docs/overview/roadmap.md +++ b/docs/overview/roadmap.md @@ -20,7 +20,7 @@ to keep us aligned as a community on: ### How to influence the roadmap As we evolve Backstage, we want you to contribute actively in the journey to -define the most effective developer experience in the world. +define the most effective developer experience in the world. A roadmap is only useful if it captures real needs. If you have success stories, feedback, or ideas, we want to hear from you! If you plan to work (or are @@ -41,8 +41,8 @@ If you have specific questions about the roadmap, please create an The Backstage roadmap lays out both [“what’s next”](#whats-next) and [“future work”](#future-work). With "next" we mean features planned for release -within the ongoing quarter from January through March 2022. With -"future" we mean features in the radar, but not yet scheduled. +within the ongoing quarter from January through March 2022. With "future" we +mean features in the radar, but not yet scheduled. The long-term roadmap (12 - 36 months) is not detailed in the public roadmap. Third-party contributions are also not currently included in the roadmap. Let us @@ -63,15 +63,16 @@ cycle will vary based on maintainer schedules. ### Backstage 1.0 (and following versions) -During the first quarter of 2022, we plan to finalize and release -version 1.0 of the Backstage platform (defined by the Core, +During the first quarter of 2022, we plan to finalize and release version 1.0 of +the Backstage platform (defined by the Core, [Catalog](https://backstage.io/docs/features/software-catalog/software-catalog-overview), [Scaffolder](https://backstage.io/docs/features/software-templates/software-templates-index) and [TechDocs](https://backstage.io/docs/features/techdocs/techdocs-overview)). Included as part of this milestone: -- Deciding on the cadence of minor/weekly/daily releases to provide clarity on the frequency - and expectations for future versions of the platform and its defining modules. +- Deciding on the cadence of minor/weekly/daily releases to provide clarity on + the frequency and expectations for future versions of the platform and its + defining modules. - Establish the support model to set the expectations from the adopters in their respective use cases. @@ -79,10 +80,10 @@ Included as part of this milestone: This initiative is the first of a broader Security Strategy for Backstage. The purpose of the Security Audit is to involve third-party companies in auditing -the platform and highlighting potential vulnerabilities. The -benefit for the adopters is clear: We want Backstage to be as secure as -possible, and we want to make it reliable through a specific initiative. This -initiative in particular is done together, and with the support of, the +the platform and highlighting potential vulnerabilities. The benefit for the +adopters is clear: We want Backstage to be as secure as possible, and we want to +make it reliable through a specific initiative. This initiative in particular is +done together, and with the support of, the [Cloud Native Computing Foundation (CNCF)](https://www.cncf.io/). ### Moving to Incubation in CNCF @@ -99,15 +100,15 @@ the maintainers’ radar, with clear interest expressed by the community. - **Backend Services:** To better scale and maintain the Backstage instances, a backend layer of services is planned to be introduced as part of the software architecture. This layer of backend services will help in decoupling the - various modules (e.g. Catalog and Scaffolder) from the frontend - experience. + various modules (e.g. Catalog and Scaffolder) from the frontend experience. - **Security Plan (and Strategy):** The purpose of the Security Strategy is to - move another step along the path to maturing the platform, - setting the expectations of any adopters from a security standpoint. + move another step along the path to maturing the platform, setting the + expectations of any adopters from a security standpoint. - **Search GA:**. - **[GraphQL](https://graphql.org/) support:** Introduce the ability to query Backstage backend services with a standard query language for APIs. -- **Telemetry:** To efficiently generate logging and metrics in such a way that adopters can get insights so that Backstage can be monitored and improved. +- **Telemetry:** To efficiently generate logging and metrics in such a way that + adopters can get insights so that Backstage can be monitored and improved. - **Improved UX design:** Provide a better Backstage user experience through visual guidelines and templates, especially navigation across plug-ins and portal functionalities. From bd2ce1922b95c501dfde6f6dd4d3d08a2c54c4ca Mon Sep 17 00:00:00 2001 From: Johan Haals Date: Fri, 14 Jan 2022 14:21:53 +0100 Subject: [PATCH 095/116] Publish pre-releases under `next` npm dist tag Signed-off-by: Johan Haals --- .github/workflows/master.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index 2a6e3dc6f9..7036f99c57 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -192,7 +192,12 @@ jobs: # Publishes current version of packages that are not already present in the registry - name: publish - run: yarn lerna -- publish from-package --yes + run: | + if [ -f ".changeset/pre.json" ]; then + yarn lerna -- publish from-package --yes --dist-tag next + else + yarn lerna -- publish from-package --yes + fi env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From dce98a92f71dbf2733128d27962be7cadfeb7b8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 14 Jan 2022 15:06:29 +0100 Subject: [PATCH 096/116] Clear parent entity hashes on deletion, to get healing behaviors back MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- .changeset/green-candles-remember.md | 5 ++ .../src/service/NextEntitiesCatalog.test.ts | 67 +++++++++++++++++++ .../src/service/NextEntitiesCatalog.ts | 23 +++++++ 3 files changed, 95 insertions(+) create mode 100644 .changeset/green-candles-remember.md diff --git a/.changeset/green-candles-remember.md b/.changeset/green-candles-remember.md new file mode 100644 index 0000000000..2edec229e4 --- /dev/null +++ b/.changeset/green-candles-remember.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +Now when entities are deleted, the parent entity state is updated such that it will "heal" accidental deletes on the next refresh round. diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts index d0f284c11c..86fabd8955 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.test.ts @@ -72,6 +72,8 @@ describe('NextEntitiesCatalog', () => { target_entity_ref: stringifyEntityRef(entity), }); } + + return id; } async function addEntityToSearch(knex: Knex, entity: Entity) { @@ -468,4 +470,69 @@ describe('NextEntitiesCatalog', () => { }, ); }); + + describe('removeEntityByUid', () => { + it.each(databases.eachSupportedId())( + 'also clears parent hashes', + async databaseId => { + const { knex } = await createDatabase(databaseId); + + const grandparent: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'grandparent' }, + spec: {}, + }; + const parent1: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'parent1' }, + spec: {}, + }; + const parent2: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'parent2' }, + spec: {}, + }; + const root: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'root' }, + spec: {}, + }; + const unrelated: Entity = { + apiVersion: 'a', + kind: 'k', + metadata: { name: 'unrelated' }, + spec: {}, + }; + + await addEntity(knex, grandparent, [{ source: 's' }]); + await addEntity(knex, parent1, [{ entity: grandparent }]); + await addEntity(knex, parent2, [{ entity: grandparent }]); + const uid = await addEntity(knex, root, [ + { entity: parent1 }, + { entity: parent2 }, + ]); + await addEntity(knex, unrelated, []); + await knex('refresh_state').update({ result_hash: 'not-changed' }); + + const catalog = new NextEntitiesCatalog(knex); + await catalog.removeEntityByUid(uid); + + await expect( + knex + .from('refresh_state') + .select('entity_ref', 'result_hash') + .orderBy('entity_ref'), + ).resolves.toEqual([ + { entity_ref: 'k:default/grandparent', result_hash: 'not-changed' }, + { entity_ref: 'k:default/parent1', result_hash: 'child-was-deleted' }, + { entity_ref: 'k:default/parent2', result_hash: 'child-was-deleted' }, + { entity_ref: 'k:default/unrelated', result_hash: 'not-changed' }, + ]); + }, + ); + }); }); diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts index f9b3abf190..28b53cf76d 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts @@ -204,6 +204,29 @@ export class NextEntitiesCatalog implements EntitiesCatalog { } async removeEntityByUid(uid: string): Promise { + // Clear the hashed state of the immediate parents of the deleted entity. + // This makes sure that when they get reprocessed, their output is written + // down again. The reason for wanting to do this, is that if the user + // deletes entities that ARE still emitted by the parent, the parent + // processing will still generate the same output hash as always, which + // means it'll never try to write down the children again (it assumes that + // they already exist). This makes the database never "heal" from accidental + // deletes. + await this.database('refresh_state') + .update({ + result_hash: 'child-was-deleted', + }) + .whereIn('entity_ref', function parents(builder) { + return builder + .from('refresh_state') + .innerJoin('refresh_state_references', { + 'refresh_state_references.target_entity_ref': + 'refresh_state.entity_ref', + }) + .where('refresh_state.entity_id', '=', uid) + .select('refresh_state_references.source_entity_ref'); + }); + await this.database('refresh_state') .where('entity_id', uid) .delete(); From 1fdf97f15043e309438b28e775af27b187c29edf Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 14 Jan 2022 16:09:50 +0100 Subject: [PATCH 097/116] Avoid breaking api changes Signed-off-by: Philipp Hugenroth --- packages/core-components/api-report.md | 9 --------- .../core-components/src/layout/Sidebar/MobileSidebar.tsx | 4 ++-- packages/core-components/src/layout/Sidebar/index.ts | 7 ++----- 3 files changed, 4 insertions(+), 16 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index f10d7102c9..b3f502e9ae 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -703,15 +703,6 @@ export type MissingAnnotationEmptyStateClassKey = 'code'; // @public export const MobileSidebar: (props: MobileSidebarProps) => JSX.Element | null; -// @public -export const MobileSidebarContext: React_2.Context; - -// @public -export type MobileSidebarContextType = { - selectedMenuItemIndex: number; - setSelectedMenuItemIndex: React_2.Dispatch>; -}; - // @public export type MobileSidebarProps = { children?: React_2.ReactNode; diff --git a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx index 298c53fb8f..3819f21a02 100644 --- a/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx +++ b/packages/core-components/src/layout/Sidebar/MobileSidebar.tsx @@ -34,7 +34,7 @@ import { SidebarGroup } from './SidebarGroup'; /** * Type of `MobileSidebarContext` * - * @public + * @internal */ export type MobileSidebarContextType = { selectedMenuItemIndex: number; @@ -134,7 +134,7 @@ const OverlayMenu = ({ /** * Context on which `SidebarGroup` is currently selected * - * @public + * @internal */ export const MobileSidebarContext = createContext({ selectedMenuItemIndex: -1, diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 82becb667f..d175b364da 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -15,11 +15,8 @@ */ export { Sidebar } from './Bar'; -export { MobileSidebar, MobileSidebarContext } from './MobileSidebar'; -export type { - MobileSidebarContextType, - MobileSidebarProps, -} from './MobileSidebar'; +export { MobileSidebar } from './MobileSidebar'; +export type { MobileSidebarProps } from './MobileSidebar'; export { SidebarGroup } from './SidebarGroup'; export type { SidebarGroupProps } from './SidebarGroup'; export { SidebarSubmenuItem } from './SidebarSubmenuItem'; From 374550ead0bb797bc8900738b50046c013f84ed1 Mon Sep 17 00:00:00 2001 From: Philipp Hugenroth Date: Fri, 14 Jan 2022 16:18:22 +0100 Subject: [PATCH 098/116] Fix wrong import path in test Signed-off-by: Philipp Hugenroth --- .../src/layout/Sidebar/SidebarGroup.test.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx index e4d7e21c50..b8f1eb5ebc 100644 --- a/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx +++ b/packages/core-components/src/layout/Sidebar/SidebarGroup.test.tsx @@ -20,12 +20,8 @@ import LayersIcon from '@material-ui/icons/Layers'; import LibraryBooks from '@material-ui/icons/LibraryBooks'; import { fireEvent } from '@testing-library/react'; import React from 'react'; -import { - MobileSidebarContext, - SidebarGroup, - SidebarItem, - SidebarPage, -} from '.'; +import { MobileSidebarContext } from './MobileSidebar'; +import { SidebarGroup, SidebarItem, SidebarPage } from '.'; const SidebarGroupWithItems = () => ( From 2908a41b9b63314c8d0752ebf5b3660bfe105885 Mon Sep 17 00:00:00 2001 From: djamaile Date: Fri, 14 Jan 2022 16:41:24 +0100 Subject: [PATCH 099/116] fix: typo in MembersListCard component Signed-off-by: djamaile --- .changeset/late-carrots-end.md | 5 +++++ .../components/Cards/Group/MembersList/MembersListCard.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/late-carrots-end.md diff --git a/.changeset/late-carrots-end.md b/.changeset/late-carrots-end.md new file mode 100644 index 0000000000..9971baffdc --- /dev/null +++ b/.changeset/late-carrots-end.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-org': patch +--- + +Fixed typo in `MembersListCard` component diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index ac2e1e0e8f..66d1042e28 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -192,7 +192,7 @@ export const MembersListCard = (_props: { ) : ( - This group has no ${memberDisplayTitle.toLocaleLowerCase()}. + This group has no {memberDisplayTitle.toLocaleLowerCase()}. )} From 653d1fe263b10d34a6383f1c5fdad79001b530d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 14 Jan 2022 16:47:27 +0100 Subject: [PATCH 100/116] better formulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- plugins/catalog-backend/src/service/NextEntitiesCatalog.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts index 28b53cf76d..d27a3be95a 100644 --- a/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts +++ b/plugins/catalog-backend/src/service/NextEntitiesCatalog.ts @@ -210,8 +210,8 @@ export class NextEntitiesCatalog implements EntitiesCatalog { // deletes entities that ARE still emitted by the parent, the parent // processing will still generate the same output hash as always, which // means it'll never try to write down the children again (it assumes that - // they already exist). This makes the database never "heal" from accidental - // deletes. + // they already exist). This means that without the code below, the database + // never "heals" from accidental deletes. await this.database('refresh_state') .update({ result_hash: 'child-was-deleted', From cd4e3c4d4edd8eb3874621c2190dba302d739078 Mon Sep 17 00:00:00 2001 From: Dominik Schwank Date: Fri, 14 Jan 2022 18:16:54 +0100 Subject: [PATCH 101/116] fix(api-docs): set url explicitly to an empty string The url argument is undefined when it is not explicitly set to an empty string. An undefined url is causing the swagger UI to fail on match checks. fixes #8895 Signed-off-by: Dominik Schwank --- .changeset/sour-spies-hug.md | 5 +++++ .../components/OpenApiDefinitionWidget/OpenApiDefinition.tsx | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/sour-spies-hug.md diff --git a/.changeset/sour-spies-hug.md b/.changeset/sour-spies-hug.md new file mode 100644 index 0000000000..eb6b860922 --- /dev/null +++ b/.changeset/sour-spies-hug.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Using an explicitly empty string for the url argument ensures that the swagger UI does not run into undefined errors. diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx index 601352f21a..ae1530f8b0 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx @@ -148,7 +148,7 @@ export const OpenApiDefinition = ({ definition }: OpenApiDefinitionProps) => { return (
- +
); }; From a686efd4692344bfcf20e37f3934c4bc13a2502a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Fri, 14 Jan 2022 18:56:34 +0100 Subject: [PATCH 102/116] yarn.lock again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- yarn.lock | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/yarn.lock b/yarn.lock index 96356cd20e..23ae734721 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2346,6 +2346,26 @@ react-use "^17.2.4" zen-observable "^0.8.15" +"@backstage/plugin-azure-devops-backend@^0.2.6": + version "0.2.6" + resolved "https://registry.npmjs.org/@backstage/plugin-azure-devops-backend/-/plugin-azure-devops-backend-0.2.6.tgz#ebd32a698ef3fb28c1f5c781ff74e20c6fda7c16" + integrity sha512-cbQW3pYZHidrKD/aoN0xAcguyCJlZrzlCW5HYu13yEme1zqiroQWlGmk6MTRQCxVSanDW5GQ9SHnVINJXSfyFw== + dependencies: + "@backstage/backend-common" "^0.10.0" + "@backstage/config" "^0.1.11" + "@backstage/plugin-azure-devops-common" "^0.1.3" + "@types/express" "^4.17.6" + azure-devops-node-api "^11.0.1" + express "^4.17.1" + express-promise-router "^4.1.0" + winston "^3.2.1" + yn "^4.0.0" + +"@backstage/plugin-azure-devops-common@^0.1.3": + version "0.1.3" + resolved "https://registry.npmjs.org/@backstage/plugin-azure-devops-common/-/plugin-azure-devops-common-0.1.3.tgz#c6b37920006cfe1ac5534693f27f74790013fb92" + integrity sha512-/lqbB433viDxeojaCX+WgoWFWEexM7GNZZfJMo9lCVwp0zhSXysMahIeV8OIwbEsAXPftLHi/6dghUfZ8+UZTQ== + "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" From f006fe252999b86ffd4ff6c373c4f07bf03b5431 Mon Sep 17 00:00:00 2001 From: djamaile Date: Fri, 14 Jan 2022 19:05:43 +0100 Subject: [PATCH 103/116] feat(org): expose pageSize prop for MembersListCard component Signed-off-by: djamaile --- .changeset/five-peaches-guess.md | 13 +++++++++++++ plugins/org/api-report.md | 2 ++ .../Cards/Group/MembersList/MembersListCard.tsx | 5 +++-- 3 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 .changeset/five-peaches-guess.md diff --git a/.changeset/five-peaches-guess.md b/.changeset/five-peaches-guess.md new file mode 100644 index 0000000000..bc278cc665 --- /dev/null +++ b/.changeset/five-peaches-guess.md @@ -0,0 +1,13 @@ +--- +'@backstage/plugin-org': patch +--- + +For the component `EntityMembersListCard` you can now specify the pageSize. For example: + +```tsx + + + +``` + +If left empty it will by default use 50. diff --git a/plugins/org/api-report.md b/plugins/org/api-report.md index c042ae38af..19f5acda69 100644 --- a/plugins/org/api-report.md +++ b/plugins/org/api-report.md @@ -27,6 +27,7 @@ export const EntityGroupProfileCard: ({ export const EntityMembersListCard: (_props: { entity?: GroupEntity | undefined; memberDisplayTitle?: string | undefined; + pageSize?: number | undefined; }) => JSX.Element; // Warning: (ae-missing-release-tag) "EntityOwnershipCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -67,6 +68,7 @@ export const GroupProfileCard: ({ export const MembersListCard: (_props: { entity?: GroupEntity; memberDisplayTitle?: string; + pageSize?: number; }) => JSX.Element; // Warning: (ae-missing-release-tag) "orgPlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) diff --git a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx index 66d1042e28..8aca65f1d4 100644 --- a/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx +++ b/plugins/org/src/components/Cards/Group/MembersList/MembersListCard.tsx @@ -112,9 +112,10 @@ export const MembersListCard = (_props: { /** @deprecated The entity is now grabbed from context instead */ entity?: GroupEntity; memberDisplayTitle?: string; + pageSize?: number; }) => { const { entity: groupEntity } = useEntity(); - let { memberDisplayTitle } = _props; + let { memberDisplayTitle, pageSize } = _props; const { metadata: { name: groupName, namespace: grpNamespace }, spec: { profile }, @@ -129,7 +130,7 @@ export const MembersListCard = (_props: { const pageChange = (_: React.ChangeEvent, pageIndex: number) => { setPage(pageIndex); }; - const pageSize = 50; + pageSize = pageSize ? pageSize : 50; memberDisplayTitle = memberDisplayTitle ? memberDisplayTitle : 'Members'; const { From 96b9e44312e6441df061bb12fecc6ac3232aa8a6 Mon Sep 17 00:00:00 2001 From: Sathish Kumar Date: Fri, 14 Jan 2022 12:38:17 -0500 Subject: [PATCH 104/116] Fixing typo error Signed-off-by: Sathish Kumar --- packages/create-app/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/create-app/CHANGELOG.md b/packages/create-app/CHANGELOG.md index 8d19c65e54..42531c4814 100644 --- a/packages/create-app/CHANGELOG.md +++ b/packages/create-app/CHANGELOG.md @@ -16,7 +16,7 @@ function makeCreateEnv(config: Config) { ... - + const permissions = ServerPerimssionClient.fromConfig(config, { + + const permissions = ServerPermissionClient.fromConfig(config, { + discovery, + tokenManager, + }); From 049cb4b1bba7695247641a48076e7eedf6a0368a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Jan 2022 06:30:07 +0000 Subject: [PATCH 105/116] build(deps): bump shelljs from 0.8.4 to 0.8.5 Bumps [shelljs](https://github.com/shelljs/shelljs) from 0.8.4 to 0.8.5. - [Release notes](https://github.com/shelljs/shelljs/releases) - [Changelog](https://github.com/shelljs/shelljs/blob/master/CHANGELOG.md) - [Commits](https://github.com/shelljs/shelljs/compare/v0.8.4...v0.8.5) --- updated-dependencies: - dependency-name: shelljs dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/yarn.lock b/yarn.lock index 23ae734721..5020d9055b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10176,9 +10176,9 @@ balanced-match@^0.4.2: integrity sha1-yz8+PHMtwPAe5wtAPzAuYddwmDg= balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.0.2, base64-js@^1.3.0, base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" @@ -16964,9 +16964,9 @@ internal-slot@^1.0.3: side-channel "^1.0.4" interpret@^1.0.0: - version "1.2.0" - resolved "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296" - integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw== + version "1.4.0" + resolved "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" + integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== interpret@^2.2.0: version "2.2.0" @@ -17146,13 +17146,20 @@ is-ci@^3.0.0: dependencies: ci-info "^3.1.1" -is-core-module@^2.1.0, is-core-module@^2.2.0, is-core-module@^2.8.0: +is-core-module@^2.1.0, is-core-module@^2.2.0: version "2.8.0" resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz#0321336c3d0925e497fd97f5d95cb114a5ccd548" integrity sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw== dependencies: has "^1.0.3" +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -22438,7 +22445,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -24920,7 +24927,16 @@ resolve-url@^0.2.1: resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.9.0: +resolve@^1.1.6: + version "1.21.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== + dependencies: + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.10.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.20.0, resolve@^1.3.2, resolve@^1.9.0: version "1.20.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -25599,9 +25615,9 @@ shell-quote@^1.7.3: integrity sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw== shelljs@^0.8.3, shelljs@^0.8.4: - version "0.8.4" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" - integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + version "0.8.5" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -26688,6 +26704,11 @@ supports-hyperlinks@^2.0.0: has-flag "^4.0.0" supports-color "^7.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svg-parser@^2.0.2: version "2.0.4" resolved "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" From 2afa6a94c0ba9776fa5b8818f5570172b57fcd0a Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 15 Jan 2022 10:45:08 +0000 Subject: [PATCH 106/116] build(deps): bump shelljs from 0.8.4 to 0.8.5 in /microsite Bumps [shelljs](https://github.com/shelljs/shelljs) from 0.8.4 to 0.8.5. - [Release notes](https://github.com/shelljs/shelljs/releases) - [Changelog](https://github.com/shelljs/shelljs/blob/master/CHANGELOG.md) - [Commits](https://github.com/shelljs/shelljs/compare/v0.8.4...v0.8.5) --- updated-dependencies: - dependency-name: shelljs dependency-type: indirect ... Signed-off-by: dependabot[bot] --- microsite/yarn.lock | 51 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/microsite/yarn.lock b/microsite/yarn.lock index b4d97d549d..01226a9bf9 100644 --- a/microsite/yarn.lock +++ b/microsite/yarn.lock @@ -1197,9 +1197,9 @@ babylon@^6.18.0: integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== base64-js@^1.3.1: version "1.5.1" @@ -3028,7 +3028,19 @@ glob-to-regexp@^0.3.0: resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@^7.1.7, glob@~7.1.1: +glob@^7.0.0, glob@^7.0.5, glob@^7.1.2, glob@^7.1.3, glob@^7.1.6, glob@^7.1.7: + version "7.2.0" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@~7.1.1: version "7.1.7" resolved "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -3556,6 +3568,13 @@ is-core-module@^2.1.0: dependencies: has "^1.0.3" +is-core-module@^2.8.0: + version "2.8.1" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211" + integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA== + dependencies: + has "^1.0.3" + is-data-descriptor@^0.1.4: version "0.1.4" resolved "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" @@ -4793,7 +4812,7 @@ path-key@^2.0.0, path-key@^2.0.1: resolved "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -5586,7 +5605,16 @@ resolve-url@^0.2.1: resolved "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@^1.1.6, resolve@^1.10.0: +resolve@^1.1.6: + version "1.21.0" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.21.0.tgz#b51adc97f3472e6a5cf4444d34bc9d6b9037591f" + integrity sha512-3wCbTpk5WJlyE4mSOtDLhqQmGFi0/TD9VPwmiolnk8U0wRgMEktqCXd3vy5buTO3tljvalNvKrjHEfrd2WpEKA== + dependencies: + is-core-module "^2.8.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve@^1.10.0: version "1.19.0" resolved "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== @@ -5781,9 +5809,9 @@ shell-quote@1.7.2: integrity sha512-mRz/m/JVscCrkMyPqHc/bczi3OQHkLTqXHEFu0zDhK/qfv3UcOA4SVmRCLmos4bhjr9ekVQubj/R7waKapmiQg== shelljs@^0.8.4: - version "0.8.4" - resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.4.tgz#de7684feeb767f8716b326078a8a00875890e3c2" - integrity sha512-7gk3UZ9kOfPLIAbslLzyWeGiEqx9e3rxwZM0KE6EL8GlGwjym9Mrlx5/p33bWTu9YG6vcS4MBxYZDHYr5lr8BQ== + version "0.8.5" + resolved "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c" + integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow== dependencies: glob "^7.0.0" interpret "^1.0.0" @@ -6133,6 +6161,11 @@ supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + svgo@^1.0.0, svgo@^1.3.2: version "1.3.2" resolved "https://registry.npmjs.org/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" From bc58af272292a771f37d97bc0f121217acb8fc2f Mon Sep 17 00:00:00 2001 From: Julio Zynger Date: Sat, 15 Jan 2022 15:47:42 +0100 Subject: [PATCH 107/116] Replace GoCD plugin logo The original link does not render on the plugins page, replace it with an asset from the official website. Signed-off-by: Julio Zynger --- microsite/data/plugins/gocd.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/microsite/data/plugins/gocd.yaml b/microsite/data/plugins/gocd.yaml index 7bf24d75fd..7c7190effc 100644 --- a/microsite/data/plugins/gocd.yaml +++ b/microsite/data/plugins/gocd.yaml @@ -5,7 +5,7 @@ authorUrl: https://github.com/soundcloud category: CI/CD description: GoCD is an open-source tool which is used in software development to help teams and organizations automate the continuous delivery of software. documentation: https://github.com/backstage/backstage/tree/master/plugins/gocd -iconUrl: https://pics.freeicons.io/uploads/icons/png/13646383971540553613-512.png +iconUrl: https://www.gocd.org/assets/images/go_logo-5b5ca9e1.svg npmPackageName: '@backstage/plugin-gocd' tags: - ci From 4316d9ae40ec53de79714a04b05097ae71702b7e Mon Sep 17 00:00:00 2001 From: Kurt King Date: Sat, 15 Jan 2022 10:36:20 -0600 Subject: [PATCH 108/116] Fixed several types in the Architecture Overview docs Signed-off-by: Kurt King --- docs/overview/architecture-overview.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index d3f171e4f8..d08d9cbdcd 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -202,12 +202,12 @@ with a smaller collection of plugins. ### Plugin Packages A typical plugin consists of up to five packages, two frontend ones, two -backend, and one isomorphic packages. All packages within the plugin must share +backend, and one isomorphic package. All packages within the plugin must share a common prefix, typically of the form `@/plugin-`, but alternatives like `backstage-plugin-` or `@scope/backstage-plugin-` are also valid. Along with this prefix, each of the packages have their own unique suffix that denotes their role. In -addition to these five plugin packages it's also possible for to a plugin to +addition to these five plugin packages it's also possible for a plugin to have additional frontend and backend modules that can be installed to enable optional features. For a full list of suffixes and their roles, see the [Plugin Package Structure ADR](../architecture-decisions/adr011-plugin-package-structure.md). @@ -230,7 +230,7 @@ The frontend packages are grouped into two main groups. The first one is as well as provide a foundation for the plugin libraries to rely upon. The second group is the rest of the shared packages, further divided into -"Frontend Plugin Core" and "Frontend Libraries". The core packages that are +"Frontend Plugin Core" and "Frontend Libraries". The core packages are considered particularly stable and form the core of the frontend framework. Their most important role is to form the boundary around each plugin and provide a set of tools that helps you combine a collection of plugins into a running @@ -246,7 +246,7 @@ however likely to change in the future. ### Common Packages -The common packages are the packages are effectively depended on by all other +The common packages are the packages effectively depended on by all other pages. This is a much smaller set of packages but they are also very pervasive. Because the common packages are isomorphic and must execute both in the frontend and backend, they are never allowed to depend on any of the frontend of backend From 838dd945da97e977397fb4b827f6f7db5234281d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sat, 15 Jan 2022 22:53:54 +0100 Subject: [PATCH 109/116] prettier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- docs/overview/architecture-overview.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index d08d9cbdcd..5d27cc4184 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -202,14 +202,14 @@ with a smaller collection of plugins. ### Plugin Packages A typical plugin consists of up to five packages, two frontend ones, two -backend, and one isomorphic package. All packages within the plugin must share -a common prefix, typically of the form `@/plugin-`, but +backend, and one isomorphic package. All packages within the plugin must share a +common prefix, typically of the form `@/plugin-`, but alternatives like `backstage-plugin-` or `@scope/backstage-plugin-` are also valid. Along with this prefix, each of the packages have their own unique suffix that denotes their role. In -addition to these five plugin packages it's also possible for a plugin to -have additional frontend and backend modules that can be installed to enable -optional features. For a full list of suffixes and their roles, see the +addition to these five plugin packages it's also possible for a plugin to have +additional frontend and backend modules that can be installed to enable optional +features. For a full list of suffixes and their roles, see the [Plugin Package Structure ADR](../architecture-decisions/adr011-plugin-package-structure.md). The `-react`, `-common`, and `-node` plugin packages together form the external @@ -246,10 +246,10 @@ however likely to change in the future. ### Common Packages -The common packages are the packages effectively depended on by all other -pages. This is a much smaller set of packages but they are also very pervasive. -Because the common packages are isomorphic and must execute both in the frontend -and backend, they are never allowed to depend on any of the frontend of backend +The common packages are the packages effectively depended on by all other pages. +This is a much smaller set of packages but they are also very pervasive. Because +the common packages are isomorphic and must execute both in the frontend and +backend, they are never allowed to depend on any of the frontend of backend packages. The Backstage CLI is in a category of its own and is depended on by virtually From de907b4f20498efffa4c55b82fb20cd76772f944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fredrik=20Adel=C3=B6w?= Date: Sun, 16 Jan 2022 20:57:06 +0100 Subject: [PATCH 110/116] fix wrong azure devops version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fredrik Adelöw --- packages/backend/package.json | 2 +- yarn.lock | 20 -------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/packages/backend/package.json b/packages/backend/package.json index c3b2236dd8..d11fd64441 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -32,7 +32,7 @@ "@backstage/integration": "^0.7.1", "@backstage/plugin-app-backend": "^0.3.21", "@backstage/plugin-auth-backend": "^0.6.2", - "@backstage/plugin-azure-devops-backend": "^0.2.6", + "@backstage/plugin-azure-devops-backend": "^0.3.0", "@backstage/plugin-badges-backend": "^0.1.15", "@backstage/plugin-catalog-backend": "^0.20.0", "@backstage/plugin-code-coverage-backend": "^0.1.19", diff --git a/yarn.lock b/yarn.lock index 5020d9055b..b5a8d898f5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2346,26 +2346,6 @@ react-use "^17.2.4" zen-observable "^0.8.15" -"@backstage/plugin-azure-devops-backend@^0.2.6": - version "0.2.6" - resolved "https://registry.npmjs.org/@backstage/plugin-azure-devops-backend/-/plugin-azure-devops-backend-0.2.6.tgz#ebd32a698ef3fb28c1f5c781ff74e20c6fda7c16" - integrity sha512-cbQW3pYZHidrKD/aoN0xAcguyCJlZrzlCW5HYu13yEme1zqiroQWlGmk6MTRQCxVSanDW5GQ9SHnVINJXSfyFw== - dependencies: - "@backstage/backend-common" "^0.10.0" - "@backstage/config" "^0.1.11" - "@backstage/plugin-azure-devops-common" "^0.1.3" - "@types/express" "^4.17.6" - azure-devops-node-api "^11.0.1" - express "^4.17.1" - express-promise-router "^4.1.0" - winston "^3.2.1" - yn "^4.0.0" - -"@backstage/plugin-azure-devops-common@^0.1.3": - version "0.1.3" - resolved "https://registry.npmjs.org/@backstage/plugin-azure-devops-common/-/plugin-azure-devops-common-0.1.3.tgz#c6b37920006cfe1ac5534693f27f74790013fb92" - integrity sha512-/lqbB433viDxeojaCX+WgoWFWEexM7GNZZfJMo9lCVwp0zhSXysMahIeV8OIwbEsAXPftLHi/6dghUfZ8+UZTQ== - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" From d6a02ec73f67466050bb8c43fe748826a79868ca Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 04:10:00 +0000 Subject: [PATCH 111/116] build(deps): bump terser-webpack-plugin from 5.2.4 to 5.3.0 Bumps [terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin) from 5.2.4 to 5.3.0. - [Release notes](https://github.com/webpack-contrib/terser-webpack-plugin/releases) - [Changelog](https://github.com/webpack-contrib/terser-webpack-plugin/blob/master/CHANGELOG.md) - [Commits](https://github.com/webpack-contrib/terser-webpack-plugin/compare/v5.2.4...v5.3.0) --- updated-dependencies: - dependency-name: terser-webpack-plugin dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- yarn.lock | 55 +++++++++++-------------------------------------------- 1 file changed, 11 insertions(+), 44 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5020d9055b..fe94d1ff3a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10700,7 +10700,7 @@ cacache@^12.0.2: unique-filename "^1.1.1" y18n "^4.0.0" -cacache@^15.0.3, cacache@^15.2.0: +cacache@^15.0.3, cacache@^15.0.5, cacache@^15.2.0: version "15.3.0" resolved "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz#dc85380fb2f556fe3dda4c719bfa0ec875a7f1eb" integrity sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ== @@ -10724,29 +10724,6 @@ cacache@^15.0.3, cacache@^15.2.0: tar "^6.0.2" unique-filename "^1.1.1" -cacache@^15.0.5: - version "15.0.5" - resolved "https://registry.npmjs.org/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" - integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.0" - tar "^6.0.2" - unique-filename "^1.1.1" - cache-base@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" @@ -18259,10 +18236,10 @@ jest-worker@^26.5.0, jest-worker@^26.6.2: merge-stream "^2.0.0" supports-color "^7.0.0" -jest-worker@^27.0.6: - version "27.2.2" - resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.2.2.tgz#636deeae8068abbf2b34b4eb9505f8d4e5bd625c" - integrity sha512-aG1xq9KgWB2CPC8YdMIlI8uZgga2LFNcGbHJxO8ctfXAydSaThR4EewKQGg3tBOC+kS3vhPGgymsBdi9VINjPw== +jest-worker@^27.4.1: + version "27.4.6" + resolved "https://registry.npmjs.org/jest-worker/-/jest-worker-27.4.6.tgz#5d2d93db419566cb680752ca0792780e71b3273e" + integrity sha512-gHWJF/6Xi5CTG5QCvROr6GcmpIqNYpDJyc8A1h/DyXqH1tD6SnRCM0d3U5msV31D2LB/U+E0M+W4oyvKV44oNw== dependencies: "@types/node" "*" merge-stream "^2.0.0" @@ -25889,7 +25866,7 @@ source-map-resolve@^0.6.0: atob "^2.1.2" decode-uri-component "^0.2.0" -source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12, source-map-support@~0.5.19: +source-map-support@^0.5.16, source-map-support@^0.5.17, source-map-support@^0.5.6, source-map-support@~0.5.12: version "0.5.19" resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== @@ -27012,12 +26989,11 @@ terminal-link@^2.0.0: supports-hyperlinks "^2.0.0" terser-webpack-plugin@*, terser-webpack-plugin@^5.1.3: - version "5.2.4" - resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.2.4.tgz#ad1be7639b1cbe3ea49fab995cbe7224b31747a1" - integrity sha512-E2CkNMN+1cho04YpdANyRrn8CyN4yMy+WdFKZIySFZrGXZxJwJP6PMNGGc/Mcr6qygQHUUqRxnAPmi0M9f00XA== + version "5.3.0" + resolved "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.0.tgz#21641326486ecf91d8054161c816e464435bae9f" + integrity sha512-LPIisi3Ol4chwAaPP8toUJ3L4qCM1G0wao7L3qNv57Drezxj6+VEyySpPw4B1HSO2Eg/hDY/MNF5XihCAoqnsQ== dependencies: - jest-worker "^27.0.6" - p-limit "^3.1.0" + jest-worker "^27.4.1" schema-utils "^3.1.1" serialize-javascript "^6.0.0" source-map "^0.6.1" @@ -27062,16 +27038,7 @@ terser@^4.1.2, terser@^4.6.3: source-map "~0.6.1" source-map-support "~0.5.12" -terser@^5.3.4: - version "5.7.1" - resolved "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz#2dc7a61009b66bb638305cb2a824763b116bf784" - integrity sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - -terser@^5.7.2: +terser@^5.3.4, terser@^5.7.2: version "5.9.0" resolved "https://registry.npmjs.org/terser/-/terser-5.9.0.tgz#47d6e629a522963240f2b55fcaa3c99083d2c351" integrity sha512-h5hxa23sCdpzcye/7b8YqbE5OwKca/ni0RQz1uRX3tGh8haaGHqcuSqbGRybuAKNdntZ0mDgFNXPJ48xQ2RXKQ== From c0e4963fd1233bcf9b6cb5996cfd94829a5eef0e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 04:15:05 +0000 Subject: [PATCH 112/116] build(deps): bump keyv from 4.0.3 to 4.0.5 Bumps [keyv](https://github.com/jaredwray/keyv) from 4.0.3 to 4.0.5. - [Release notes](https://github.com/jaredwray/keyv/releases) - [Commits](https://github.com/jaredwray/keyv/commits) --- updated-dependencies: - dependency-name: keyv dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5020d9055b..4d32f43928 100644 --- a/yarn.lock +++ b/yarn.lock @@ -18896,9 +18896,9 @@ keyv@^3.0.0: json-buffer "3.0.0" keyv@^4.0.0, keyv@^4.0.3: - version "4.0.3" - resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.3.tgz#4f3aa98de254803cafcd2896734108daa35e4254" - integrity sha512-zdGa2TOpSZPq5mU6iowDARnMBZgtCqJ11dJROFi6tg6kTn4nuUdU09lFyLFSaHrWqpIJ+EBq4E8/Dc0Vx5vLdA== + version "4.0.5" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.0.5.tgz#bb12b467aba372fab2a44d4420c00d3c4ebd484c" + integrity sha512-531pkGLqV3BMg0eDqqJFI0R1mkK1Nm5xIP2mM6keP5P8WfFtCkg2IOwplTUmlGoTgIg9yQYZ/kdihhz89XH3vA== dependencies: json-buffer "3.0.1" From dc6aa5559ff5372a3c05dd690a8ed71a802f65da Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 04:18:55 +0000 Subject: [PATCH 113/116] build(deps-dev): bump @types/jquery from 3.5.8 to 3.5.13 Bumps [@types/jquery](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jquery) from 3.5.8 to 3.5.13. - [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases) - [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jquery) --- updated-dependencies: - dependency-name: "@types/jquery" dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5020d9055b..657d06c830 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7647,9 +7647,9 @@ pretty-format "^26.0.0" "@types/jquery@^3.3.34": - version "3.5.8" - resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.8.tgz#83bfbcdf4e625c5471590f92703c06aadb052a09" - integrity sha512-cXk6NwqjDYg+UI9p2l3x0YmPa4m7RrXqmbK4IpVVpRJiYXU/QTo+UZrn54qfE1+9Gao4qpYqUnxm5ZCy2FTXAw== + version "3.5.13" + resolved "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.13.tgz#5482d3ee325d5862f77a91c09369ae0a5b082bf3" + integrity sha512-ZxJrup8nz/ZxcU0vantG+TPdboMhB24jad2uSap50zE7Q9rUeYlCF25kFMSmHR33qoeOgqcdHEp3roaookC0Sg== dependencies: "@types/sizzle" "*" From 28872b53dd9a9db3a336a225d1b189ef25e26d52 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 04:19:55 +0000 Subject: [PATCH 114/116] build(deps): bump postcss from 8.3.5 to 8.4.5 Bumps [postcss](https://github.com/postcss/postcss) from 8.3.5 to 8.4.5. - [Release notes](https://github.com/postcss/postcss/releases) - [Changelog](https://github.com/postcss/postcss/blob/main/CHANGELOG.md) - [Commits](https://github.com/postcss/postcss/compare/8.3.5...8.4.5) --- updated-dependencies: - dependency-name: postcss dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- yarn.lock | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5020d9055b..e6d66f6298 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11493,7 +11493,7 @@ colorette@1.2.1: resolved "https://registry.npmjs.org/colorette/-/colorette-1.2.1.tgz#4d0b921325c14faf92633086a536db6e89564b1b" integrity sha512-puCDz0CzydiSYOrnXpz/PKd69zRrribezjtE9yd4zvytoRc8+RY/KJPvtPFKZS3E3wP6neGyMe0vOTlHO5L3Pw== -colorette@^1.2.1, colorette@^1.2.2, colorette@^1.4.0: +colorette@^1.2.1, colorette@^1.4.0: version "1.4.0" resolved "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40" integrity sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g== @@ -20994,10 +20994,10 @@ nanoclone@^0.2.1: resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== -nanoid@^3.1.23: - version "3.1.23" - resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.1.23.tgz#f744086ce7c2bc47ee0a8472574d5c78e4183a81" - integrity sha512-FiB0kzdP0FFVGDKlRLEQ1BgDzU87dy5NnzjeW9YZNt+/c3+q82EQDUwniSAUxp/F0gFNI1ZhKU1FqYsMuqZVnw== +nanoid@^3.1.23, nanoid@^3.1.30: + version "3.2.0" + resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.2.0.tgz#62667522da6673971cca916a6d3eff3f415ff80c" + integrity sha512-fmsZYa9lpn69Ad5eDn7FMcnnSR+8R34W9qJEijxYhTbfOWzr22n1QxCMzXLK+ODyW2973V3Fux959iQoUxzUIA== nanomatch@^1.2.9: version "1.2.13" @@ -23113,13 +23113,13 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^ supports-color "^6.1.0" postcss@^8.1.0, postcss@^8.2.15: - version "8.3.5" - resolved "https://registry.npmjs.org/postcss/-/postcss-8.3.5.tgz#982216b113412bc20a86289e91eb994952a5b709" - integrity sha512-NxTuJocUhYGsMiMFHDUkmjSKT3EdH4/WbGF6GCi1NDGk+vbcUTun4fpbOqaPtD8IIsztA2ilZm2DhYCuyN58gA== + version "8.4.5" + resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.5.tgz#bae665764dfd4c6fcc24dc0fdf7e7aa00cc77f95" + integrity sha512-jBDboWM8qpaqwkMwItqTQTiFikhs/67OYVvblFFTM7MrZjt6yMKd6r2kgXizEbTTljacm4NldIlZnhbjr84QYg== dependencies: - colorette "^1.2.2" - nanoid "^3.1.23" - source-map-js "^0.6.2" + nanoid "^3.1.30" + picocolors "^1.0.0" + source-map-js "^1.0.1" postgres-array@~2.0.0: version "2.0.0" @@ -25865,10 +25865,10 @@ source-list-map@^2.0.0, source-list-map@^2.0.1: resolved "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== -source-map-js@^0.6.2: - version "0.6.2" - resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-0.6.2.tgz#0bb5de631b41cfbda6cfba8bd05a80efdfd2385e" - integrity sha512-/3GptzWzu0+0MBQFrDKzw/DvvMTUORvgY6k6jd/VS6iCR4RDTKWH6v6WPwQoUO8667uQEf9Oe38DxAYWY5F/Ug== +source-map-js@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf" + integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA== source-map-resolve@^0.5.0: version "0.5.3" From bf177df9c39b7d6b6a2977f81b8579adcba54511 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 04:22:35 +0000 Subject: [PATCH 115/116] build(deps): bump core-js from 3.19.0 to 3.20.3 Bumps [core-js](https://github.com/zloirock/core-js) from 3.19.0 to 3.20.3. - [Release notes](https://github.com/zloirock/core-js/releases) - [Changelog](https://github.com/zloirock/core-js/blob/master/CHANGELOG.md) - [Commits](https://github.com/zloirock/core-js/compare/v3.19.0...v3.20.3) --- updated-dependencies: - dependency-name: core-js dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 5020d9055b..57f361eff6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11966,9 +11966,9 @@ core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.10: integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: - version "3.19.0" - resolved "https://registry.npmjs.org/core-js/-/core-js-3.19.0.tgz#9e40098a9bc326c7e81b486abbd5e12b9d275176" - integrity sha512-L1TpFRWXZ76vH1yLM+z6KssLZrP8Z6GxxW4auoCj+XiViOzNPJCAuTIkn03BGdFe6Z5clX5t64wRIRypsZQrUg== + version "3.20.3" + resolved "https://registry.npmjs.org/core-js/-/core-js-3.20.3.tgz#c710d0a676e684522f3db4ee84e5e18a9d11d69a" + integrity sha512-vVl8j8ph6tRS3B8qir40H7yw7voy17xL0piAjlbBUsH7WIfzoedL/ZOr1OV9FyZQLWXsayOJyV4tnRyXR85/ag== core-util-is@1.0.2, core-util-is@~1.0.0: version "1.0.2" From 258699f1ce9529980d34253ca47621df9dccd63f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Jan 2022 08:47:02 +0000 Subject: [PATCH 116/116] build(deps): bump @svgr/plugin-jsx from 5.5.0 to 6.2.0 Bumps [@svgr/plugin-jsx](https://github.com/gregberge/svgr) from 5.5.0 to 6.2.0. - [Release notes](https://github.com/gregberge/svgr/releases) - [Changelog](https://github.com/gregberge/svgr/blob/main/CHANGELOG.md) - [Commits](https://github.com/gregberge/svgr/compare/v5.5.0...v6.2.0) --- updated-dependencies: - dependency-name: "@svgr/plugin-jsx" dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- packages/cli/package.json | 2 +- yarn.lock | 118 +++++++++++++++++++++++++++++--------- 2 files changed, 92 insertions(+), 28 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 4df006244b..95cb4f70de 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -45,7 +45,7 @@ "@spotify/eslint-config-typescript": "^12.0.0", "@sucrase/jest-plugin": "^2.1.1", "@sucrase/webpack-loader": "^2.0.0", - "@svgr/plugin-jsx": "5.5.x", + "@svgr/plugin-jsx": "6.2.x", "@svgr/plugin-svgo": "6.2.x", "@svgr/rollup": "5.5.x", "@svgr/webpack": "5.5.x", diff --git a/yarn.lock b/yarn.lock index 2623c14673..c8b506927b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -362,28 +362,7 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.7.5": - version "7.14.8" - resolved "https://registry.npmjs.org/@babel/core/-/core-7.14.8.tgz#20cdf7c84b5d86d83fac8710a8bc605a7ba3f010" - integrity sha512-/AtaeEhT6ErpDhInbXmjHcUQXH0L0TEgscfcxk1qbOvLuKCa5aZT0SOOtDKFY96/CLROwbLSKyFor6idgNaU4Q== - dependencies: - "@babel/code-frame" "^7.14.5" - "@babel/generator" "^7.14.8" - "@babel/helper-compilation-targets" "^7.14.5" - "@babel/helper-module-transforms" "^7.14.8" - "@babel/helpers" "^7.14.8" - "@babel/parser" "^7.14.8" - "@babel/template" "^7.14.5" - "@babel/traverse" "^7.14.8" - "@babel/types" "^7.14.8" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/core@^7.14.0": +"@babel/core@^7.1.0", "@babel/core@^7.12.10", "@babel/core@^7.12.3", "@babel/core@^7.13.16", "@babel/core@^7.14.0", "@babel/core@^7.15.5", "@babel/core@^7.7.5": version "7.16.7" resolved "https://registry.npmjs.org/@babel/core/-/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf" integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA== @@ -404,7 +383,7 @@ semver "^6.3.0" source-map "^0.5.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.8", "@babel/generator@^7.14.9": +"@babel/generator@^7.12.11", "@babel/generator@^7.12.5", "@babel/generator@^7.14.9": version "7.14.9" resolved "https://registry.npmjs.org/@babel/generator/-/generator-7.14.9.tgz#23b19c597d38b4f7dc2e3fe42a69c88d9ecfaa16" integrity sha512-4yoHbhDYzFa0GLfCzLp5GxH7vPPMAHdZjyE7M/OajM9037zhx0rf+iNsJwp4PT0MSFpwjG7BsHEbPkBQpZ6cYA== @@ -685,7 +664,7 @@ dependencies: "@babel/types" "^7.16.7" -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.14.5", "@babel/helper-module-transforms@^7.14.8": +"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.14.5": version "7.14.8" resolved "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.14.8.tgz#d4279f7e3fd5f4d5d342d833af36d4dd87d7dc49" integrity sha512-RyE+NFOjXn5A9YU1dkpeBaduagTlZ0+fccnIcAGbv1KGUlReBj7utF7oEth8IdIBQPcux0DDgW5MFBH2xu9KcA== @@ -890,7 +869,7 @@ "@babel/traverse" "^7.14.5" "@babel/types" "^7.14.5" -"@babel/helpers@^7.12.5", "@babel/helpers@^7.14.8": +"@babel/helpers@^7.12.5": version "7.14.8" resolved "https://registry.npmjs.org/@babel/helpers/-/helpers-7.14.8.tgz#839f88f463025886cff7f85a35297007e2da1b77" integrity sha512-ZRDmI56pnV+p1dH6d+UN6GINGz7Krps3+270qqI9UJ4wxYThfAIcI5i7j5vXC4FJ3Wap+S9qcebxeYiqn87DZw== @@ -922,7 +901,7 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.16.4.tgz#d5f92f57cf2c74ffe9b37981c0e72fee7311372e" integrity sha512-6V0qdPUaiVHH3RtZeLIsc+6pDhbYzHR8ogA8w+f+Wc77DuXto19g2QUwveINoS34Uw+W8/hQDGJCx+i4n7xcng== -"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.13.16", "@babel/parser@^7.14.2", "@babel/parser@^7.14.5", "@babel/parser@^7.14.8", "@babel/parser@^7.14.9": +"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.12.13", "@babel/parser@^7.12.7", "@babel/parser@^7.13.16", "@babel/parser@^7.14.2", "@babel/parser@^7.14.5", "@babel/parser@^7.14.9": version "7.14.9" resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.14.9.tgz#596c1ad67608070058ebf8df50c1eaf65db895a4" integrity sha512-RdUTOseXJ8POjjOeEBEvNMIZU/nm4yu2rufRkcibzkkg7DmQvXU8v3M4Xk9G7uuI86CDGkKcuDWgioqZm+mScQ== @@ -2301,6 +2280,14 @@ "@babel/helper-validator-identifier" "^7.14.9" to-fast-properties "^2.0.0" +"@babel/types@^7.15.6": + version "7.16.8" + resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1" + integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg== + dependencies: + "@babel/helper-validator-identifier" "^7.16.7" + to-fast-properties "^2.0.0" + "@babel/types@^7.16.0", "@babel/types@^7.16.7": version "7.16.7" resolved "https://registry.npmjs.org/@babel/types/-/types-7.16.7.tgz#4ed19d51f840ed4bd5645be6ce40775fecf03159" @@ -6766,41 +6753,81 @@ resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-5.4.0.tgz#81ef61947bb268eb9d50523446f9c638fb355906" integrity sha512-ZFf2gs/8/6B8PnSofI0inYXr2SDNTDScPXhN7k5EqD4aZ3gi6u+rbmZHVB8IM3wDyx8ntKACZbtXSm7oZGRqVg== +"@svgr/babel-plugin-add-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-6.0.0.tgz#bd6d1ff32a31b82b601e73672a789cc41e84fe18" + integrity sha512-MdPdhdWLtQsjd29Wa4pABdhWbaRMACdM1h31BY+c6FghTZqNGT7pEYdBoaGeKtdTOBC/XNFQaKVj+r/Ei2ryWA== + "@svgr/babel-plugin-remove-jsx-attribute@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-5.4.0.tgz#6b2c770c95c874654fd5e1d5ef475b78a0a962ef" integrity sha512-yaS4o2PgUtwLFGTKbsiAy6D0o3ugcUhWK0Z45umJ66EPWunAz9fuFw2gJuje6wqQvQWOTJvIahUwndOXb7QCPg== +"@svgr/babel-plugin-remove-jsx-attribute@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-attribute/-/babel-plugin-remove-jsx-attribute-6.0.0.tgz#58654908beebfa069681a83332544b17e5237e89" + integrity sha512-aVdtfx9jlaaxc3unA6l+M9YRnKIZjOhQPthLKqmTXC8UVkBLDRGwPKo+r8n3VZN8B34+yVajzPTZ+ptTSuZZCw== + "@svgr/babel-plugin-remove-jsx-empty-expression@^5.0.1": version "5.0.1" resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-5.0.1.tgz#25621a8915ed7ad70da6cea3d0a6dbc2ea933efd" integrity sha512-LA72+88A11ND/yFIMzyuLRSMJ+tRKeYKeQ+mR3DcAZ5I4h5CPWN9AHyUzJbWSYp/u2u0xhmgOe0+E41+GjEueA== +"@svgr/babel-plugin-remove-jsx-empty-expression@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-remove-jsx-empty-expression/-/babel-plugin-remove-jsx-empty-expression-6.0.0.tgz#d06dd6e8a8f603f92f9979bb9990a1f85a4f57ba" + integrity sha512-Ccj42ApsePD451AZJJf1QzTD1B/BOU392URJTeXFxSK709i0KUsGtbwyiqsKu7vsYxpTM0IA5clAKDyf9RCZyA== + "@svgr/babel-plugin-replace-jsx-attribute-value@^5.0.1": version "5.0.1" resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-5.0.1.tgz#0b221fc57f9fcd10e91fe219e2cd0dd03145a897" integrity sha512-PoiE6ZD2Eiy5mK+fjHqwGOS+IXX0wq/YDtNyIgOrc6ejFnxN4b13pRpiIPbtPwHEc+NT2KCjteAcq33/F1Y9KQ== +"@svgr/babel-plugin-replace-jsx-attribute-value@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-replace-jsx-attribute-value/-/babel-plugin-replace-jsx-attribute-value-6.0.0.tgz#0b85837577b02c31c09c758a12932820f5245cee" + integrity sha512-88V26WGyt1Sfd1emBYmBJRWMmgarrExpKNVmI9vVozha4kqs6FzQJ/Kp5+EYli1apgX44518/0+t9+NU36lThQ== + "@svgr/babel-plugin-svg-dynamic-title@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-5.4.0.tgz#139b546dd0c3186b6e5db4fefc26cb0baea729d7" integrity sha512-zSOZH8PdZOpuG1ZVx/cLVePB2ibo3WPpqo7gFIjLV9a0QsuQAzJiwwqmuEdTaW2pegyBE17Uu15mOgOcgabQZg== +"@svgr/babel-plugin-svg-dynamic-title@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-dynamic-title/-/babel-plugin-svg-dynamic-title-6.0.0.tgz#28236ec26f7ab9d486a487d36ae52d58ba15676f" + integrity sha512-F7YXNLfGze+xv0KMQxrl2vkNbI9kzT9oDK55/kUuymh1ACyXkMV+VZWX1zEhSTfEKh7VkHVZGmVtHg8eTZ6PRg== + "@svgr/babel-plugin-svg-em-dimensions@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-5.4.0.tgz#6543f69526632a133ce5cabab965deeaea2234a0" integrity sha512-cPzDbDA5oT/sPXDCUYoVXEmm3VIoAWAPT6mSPTJNbQaBNUuEKVKyGH93oDY4e42PYHRW67N5alJx/eEol20abw== +"@svgr/babel-plugin-svg-em-dimensions@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-svg-em-dimensions/-/babel-plugin-svg-em-dimensions-6.0.0.tgz#40267c5dea1b43c4f83a0eb6169e08b43d8bafce" + integrity sha512-+rghFXxdIqJNLQK08kwPBD3Z22/0b2tEZ9lKiL/yTfuyj1wW8HUXu4bo/XkogATIYuXSghVQOOCwURXzHGKyZA== + "@svgr/babel-plugin-transform-react-native-svg@^5.4.0": version "5.4.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-5.4.0.tgz#00bf9a7a73f1cad3948cdab1f8dfb774750f8c80" integrity sha512-3eYP/SaopZ41GHwXma7Rmxcv9uRslRDTY1estspeB1w1ueZWd/tPlMfEOoccYpEMZU3jD4OU7YitnXcF5hLW2Q== +"@svgr/babel-plugin-transform-react-native-svg@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-react-native-svg/-/babel-plugin-transform-react-native-svg-6.0.0.tgz#eb688d0a5f539e34d268d8a516e81f5d7fede7c9" + integrity sha512-VaphyHZ+xIKv5v0K0HCzyfAaLhPGJXSk2HkpYfXIOKb7DjLBv0soHDxNv6X0vr2titsxE7klb++u7iOf7TSrFQ== + "@svgr/babel-plugin-transform-svg-component@^5.5.0": version "5.5.0" resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-5.5.0.tgz#583a5e2a193e214da2f3afeb0b9e8d3250126b4a" integrity sha512-q4jSH1UUvbrsOtlo/tKcgSeiCHRSBdXoIoqX1pgcKK/aU3JD27wmMKwGtpB8qRYUYoyXvfGxUVKchLuR5pB3rQ== +"@svgr/babel-plugin-transform-svg-component@^6.2.0": + version "6.2.0" + resolved "https://registry.npmjs.org/@svgr/babel-plugin-transform-svg-component/-/babel-plugin-transform-svg-component-6.2.0.tgz#7ba61d9fc1fb42b0ba1a04e4630019fa7e993c4f" + integrity sha512-bhYIpsORb++wpsp91fymbFkf09Z/YEKR0DnFjxvN+8JHeCUD2unnh18jIMKnDJTWtvpTaGYPXELVe4OOzFI0xg== + "@svgr/babel-preset@^5.5.0": version "5.5.0" resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-5.5.0.tgz#8af54f3e0a8add7b1e2b0fcd5a882c55393df327" @@ -6815,6 +6842,20 @@ "@svgr/babel-plugin-transform-react-native-svg" "^5.4.0" "@svgr/babel-plugin-transform-svg-component" "^5.5.0" +"@svgr/babel-preset@^6.2.0": + version "6.2.0" + resolved "https://registry.npmjs.org/@svgr/babel-preset/-/babel-preset-6.2.0.tgz#1d3ad8c7664253a4be8e4a0f0e6872f30d8af627" + integrity sha512-4WQNY0J71JIaL03DRn0vLiz87JXx0b9dYm2aA8XHlQJQoixMl4r/soYHm8dsaJZ3jWtkCiOYy48dp9izvXhDkQ== + dependencies: + "@svgr/babel-plugin-add-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-attribute" "^6.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression" "^6.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value" "^6.0.0" + "@svgr/babel-plugin-svg-dynamic-title" "^6.0.0" + "@svgr/babel-plugin-svg-em-dimensions" "^6.0.0" + "@svgr/babel-plugin-transform-react-native-svg" "^6.0.0" + "@svgr/babel-plugin-transform-svg-component" "^6.2.0" + "@svgr/core@^5.5.0": version "5.5.0" resolved "https://registry.npmjs.org/@svgr/core/-/core-5.5.0.tgz#82e826b8715d71083120fe8f2492ec7d7874a579" @@ -6831,7 +6872,25 @@ dependencies: "@babel/types" "^7.12.6" -"@svgr/plugin-jsx@5.5.x", "@svgr/plugin-jsx@^5.5.0": +"@svgr/hast-util-to-babel-ast@^6.0.0": + version "6.0.0" + resolved "https://registry.npmjs.org/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.0.0.tgz#423329ad866b6c169009cc82b5e28ffee80c857c" + integrity sha512-S+TxtCdDyRGafH1VG1t/uPZ87aOYOHzWL8kqz4FoSZcIbzWA6rnOmjNViNiDzqmEpzp2PW5o5mZfvC9DiVZhTQ== + dependencies: + "@babel/types" "^7.15.6" + entities "^3.0.1" + +"@svgr/plugin-jsx@6.2.x": + version "6.2.0" + resolved "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-6.2.0.tgz#5e41a75b12b34cb66509e63e535606161770ff42" + integrity sha512-QJDEe7K5Hkd4Eewu4pcjiOKTCtjB47Ol6lDLXVhf+jEewi+EKJAaAmM+bNixfW6LSNEg8RwOYQN3GZcprqKfHw== + dependencies: + "@babel/core" "^7.15.5" + "@svgr/babel-preset" "^6.2.0" + "@svgr/hast-util-to-babel-ast" "^6.0.0" + svg-parser "^2.0.2" + +"@svgr/plugin-jsx@^5.5.0": version "5.5.0" resolved "https://registry.npmjs.org/@svgr/plugin-jsx/-/plugin-jsx-5.5.0.tgz#1aa8cd798a1db7173ac043466d7b52236b369000" integrity sha512-V/wVh33j12hGh05IDg8GpIUXbjAPnTdPTKuP4VNLggnwaHMPNQNae2pRnyTAILWCQdz5GyMqtO488g7CKM8CBA== @@ -13569,6 +13628,11 @@ entities@^2.0.0, entities@~2.1.0: resolved "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== +entities@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + env-paths@^2.2.0: version "2.2.0" resolved "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz#cdca557dc009152917d6166e2febe1f039685e43"