From ea0f921cd3008e59dd33c1fc1d370af515b3cb5c Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Fri, 8 Oct 2021 17:08:03 +0100 Subject: [PATCH 001/975] Implement Expedias new Scalable Side Nav design - items with submenus and expand on click Signed-off-by: hiba-aldalaty --- packages/app/src/components/Root/Root.tsx | 2 + .../src/layout/Sidebar/Bar.tsx | 130 +++++++------- .../src/layout/Sidebar/Items.tsx | 167 ++++++++++++++++- .../src/layout/Sidebar/Sidebar.stories.tsx | 40 ++++- .../src/layout/Sidebar/Submenu.tsx | 97 ++++++++++ .../src/layout/Sidebar/SubmenuItem.tsx | 170 ++++++++++++++++++ .../src/layout/Sidebar/config.ts | 33 +++- .../src/layout/Sidebar/icons/APIsIcon.tsx | 33 ++++ .../Sidebar/icons/CatalogSidebarLogo.tsx | 40 +++++ .../layout/Sidebar/icons/DoubleArrowLeft.tsx | 50 ++++++ .../layout/Sidebar/icons/DoubleArrowRight.tsx | 50 ++++++ .../src/layout/Sidebar/icons/MiscIcon.tsx | 33 ++++ .../src/layout/Sidebar/icons/ServicesIcon.tsx | 33 ++++ .../src/layout/Sidebar/index.ts | 3 +- packages/theme/src/themes.ts | 18 ++ packages/theme/src/types.ts | 9 + 16 files changed, 834 insertions(+), 74 deletions(-) create mode 100644 packages/core-components/src/layout/Sidebar/Submenu.tsx create mode 100644 packages/core-components/src/layout/Sidebar/SubmenuItem.tsx create mode 100644 packages/core-components/src/layout/Sidebar/icons/APIsIcon.tsx create mode 100644 packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx create mode 100644 packages/core-components/src/layout/Sidebar/icons/DoubleArrowLeft.tsx create mode 100644 packages/core-components/src/layout/Sidebar/icons/DoubleArrowRight.tsx create mode 100644 packages/core-components/src/layout/Sidebar/icons/MiscIcon.tsx create mode 100644 packages/core-components/src/layout/Sidebar/icons/ServicesIcon.tsx diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index 7a666a020a..b0318cdaf8 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -40,6 +40,7 @@ import { SidebarDivider, SidebarSpace, SidebarScrollWrapper, + SidebarExpandButton, } from '@backstage/core-components'; const useSidebarLogoStyles = makeStyles({ @@ -98,6 +99,7 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( + diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 750ec6bbdc..ad73b52435 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -16,10 +16,17 @@ import { makeStyles, useMediaQuery } from '@material-ui/core'; import clsx from 'clsx'; -import React, { useRef, useState, useContext, PropsWithChildren } from 'react'; +import React, { + useState, + useContext, + PropsWithChildren, + useEffect, +} from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; +import DoubleArrowRight from './icons/DoubleArrowRight'; +import DoubleArrowLeft from './icons/DoubleArrowLeft'; export type SidebarClassKey = 'root' | 'drawer' | 'drawerOpen'; @@ -45,7 +52,6 @@ const useStyles = makeStyles( msOverflowStyle: 'none', scrollbarWidth: 'none', width: sidebarConfig.drawerWidthClosed, - borderRight: `1px solid #383838`, transition: theme.transitions.create('width', { easing: theme.transitions.easing.sharp, duration: theme.transitions.duration.shortest, @@ -57,6 +63,19 @@ const useStyles = makeStyles( display: 'none', }, }, + expandButton: { + background: 'none', + border: 'none', + color: theme.palette.navigation.color, + width: '100%', + cursor: 'pointer', + position: 'relative', + height: 48, + }, + arrows: { + position: 'absolute', + right: 10, + }, drawerOpen: { width: sidebarConfig.drawerWidthOpen, transition: theme.transitions.create('width', { @@ -68,81 +87,24 @@ const useStyles = makeStyles( { name: 'BackstageSidebar' }, ); -enum State { - Closed, - Idle, - Open, -} - -type Props = { - openDelayMs?: number; - closeDelayMs?: number; -}; - -export function Sidebar(props: PropsWithChildren) { - const { - openDelayMs = sidebarConfig.defaultOpenDelayMs, - closeDelayMs = sidebarConfig.defaultCloseDelayMs, - children, - } = props; +export function Sidebar({ children }: PropsWithChildren<{}>) { const classes = useStyles(); - const isSmallScreen = useMediaQuery(theme => - theme.breakpoints.down('md'), - ); - const [state, setState] = useState(State.Closed); - const hoverTimerRef = useRef(); + const { isPinned } = useContext(SidebarPinStateContext); + const [isOpen, setIsOpen] = useState(isPinned); - const handleOpen = () => { + useEffect(() => { if (isPinned) { - return; + setIsOpen(true); } - if (hoverTimerRef.current) { - clearTimeout(hoverTimerRef.current); - hoverTimerRef.current = undefined; - } - if (state !== State.Open && !isSmallScreen) { - hoverTimerRef.current = window.setTimeout(() => { - hoverTimerRef.current = undefined; - setState(State.Open); - }, openDelayMs); - - setState(State.Idle); - } - }; - - const handleClose = () => { - if (isPinned) { - return; - } - if (hoverTimerRef.current) { - clearTimeout(hoverTimerRef.current); - hoverTimerRef.current = undefined; - } - if (state === State.Idle) { - setState(State.Closed); - } else if (state === State.Open) { - hoverTimerRef.current = window.setTimeout(() => { - hoverTimerRef.current = undefined; - setState(State.Closed); - }, closeDelayMs); - } - }; - - const isOpen = (state === State.Open && !isSmallScreen) || isPinned; + }, [isPinned]); return ( -
+
) {
); } + +export const SidebarExpandButton = () => { + const classes = useStyles(); + const isSmallScreen = useMediaQuery(theme => + theme.breakpoints.down('md'), + ); + const { isPinned } = useContext(SidebarPinStateContext); + const { isOpen, setIsOpen } = useContext(SidebarContext); + + const openDelayMs = sidebarConfig.defaultOpenDelayMs; + const closeDelayMs = sidebarConfig.defaultCloseDelayMs; + + const handleClick = () => { + if (isPinned || isSmallScreen) { + return; + } + const delayMs = isOpen ? openDelayMs : closeDelayMs; + setTimeout(async () => { + setIsOpen(!isOpen); + }, delayMs); + }; + + if (isSmallScreen || isPinned) { + return null; + } + + return ( + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 8bd80b6825..e735468e95 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -25,11 +25,14 @@ import { Typography, } from '@material-ui/core'; import { CreateCSSProperties } from '@material-ui/core/styles/withStyles'; +import ArrowRightIcon from '@material-ui/icons/ArrowRight'; import SearchIcon from '@material-ui/icons/Search'; import clsx from 'clsx'; import React, { + Children, forwardRef, KeyboardEventHandler, + PropsWithChildren, ReactNode, useContext, useState, @@ -37,10 +40,16 @@ import React, { import { Link, NavLinkProps, + resolvePath, useLocation, useResolvedPath, } from 'react-router-dom'; -import { sidebarConfig, SidebarContext } from './config'; +import { + sidebarConfig, + SidebarContext, + ItemWithSubmenuContext, +} from './config'; +import { Submenu } from './Submenu'; export type SidebarItemClassKey = | 'root' @@ -89,6 +98,14 @@ const useStyles = makeStyles( open: { width: drawerWidthOpen, }, + highlightable: { + '&:hover': { + background: theme.palette.navigation.navItem.hoverBackground, // TODO: consider + }, + }, + highlighted: { + background: theme.palette.navigation.navItem.hoverBackground, // TODO: consider + }, label: { // XXX (@koroeskohr): I can't seem to achieve the desired font-weight from the designs fontWeight: 'bold', @@ -127,13 +144,24 @@ const useStyles = makeStyles( textAlign: 'center', marginRight: theme.spacing(1), }, + closedItemIcon: { + width: '100%', + justifyContent: 'center', + }, + submenuArrow: { + position: 'absolute', + right: 0, + }, selected: { '&$root': { borderLeft: `solid ${selectedIndicatorWidth}px ${theme.palette.navigation.indicator}`, color: theme.palette.navigation.selectedColor, }, '&$closed': { - width: drawerWidthClosed - selectedIndicatorWidth, + width: drawerWidthClosed, + }, + '& $closedItemIcon': { + paddingRight: selectedIndicatorWidth, }, '& $iconContainer': { marginLeft: -selectedIndicatorWidth, @@ -144,10 +172,117 @@ const useStyles = makeStyles( { name: 'BackstageSidebarItem' }, ); +type ItemWithSubmenuProps = { + label?: string; + title?: string; + hasNotifications?: boolean; + icon: IconComponent; + children: ReactNode; +}; +const ItemWithSubmenu = ({ + label, + title, + hasNotifications = false, + icon: Icon, + children, +}: PropsWithChildren) => { + const classes = useStyles(); + const [isHoveredOn, setIsHoveredOn] = useState(false); + const toPathnames: string[] = []; + + let isActive; + const { pathname: locationPathname } = useLocation(); + + // Menu item is active if any of its children have active paths + Children.forEach(children, element => { + if (!React.isValidElement(element)) return; + if (element.props.hasDropDown && element.props.dropdownItems) { + element.props.dropdownItems.map((item: { to: string }) => + toPathnames.push(item.to), + ); + } else if (element.props.to) { + toPathnames.push(element.props.to); + } + }); + toPathnames.some(to => { + const toPathname = resolvePath(to); + isActive = locationPathname === toPathname.pathname; + return isActive; + }); + + const handleMouseEnter = () => { + setIsHoveredOn(true); + }; + const handleMouseLeave = () => { + setIsHoveredOn(false); + }; + + const { isOpen } = useContext(SidebarContext); + const itemIcon = ( + + + + ); + const openContent = ( + <> +
+ {itemIcon} +
+ {label && ( + + {label} + + )} +
{}
+ + ); + const closedContent = itemIcon; + + return ( + +
+
+ {isOpen ? openContent : closedContent} + {!isHoveredOn && ( + + )} +
+ {isHoveredOn && {children}} +
+
+ ); +}; + type SidebarItemBaseProps = { icon: IconComponent; text?: string; hasNotifications?: boolean; + hasSubMenu?: boolean; + submenuTitle?: string; + disableHighlight?: boolean; children?: ReactNode; className?: string; }; @@ -222,6 +357,9 @@ export const SidebarItem = forwardRef((props, ref) => { icon: Icon, text, hasNotifications = false, + hasSubMenu = false, + submenuTitle, + disableHighlight = false, onClick, children, className, @@ -239,6 +377,7 @@ export const SidebarItem = forwardRef((props, ref) => { variant="dot" overlap="circular" invisible={!hasNotifications} + className={clsx(isOpen ? undefined : classes.closedItemIcon)} > @@ -248,7 +387,7 @@ export const SidebarItem = forwardRef((props, ref) => { const openContent = ( <> -
+
{itemIcon}
{text && ( @@ -269,9 +408,24 @@ export const SidebarItem = forwardRef((props, ref) => { classes.root, isOpen ? classes.open : classes.closed, isButtonItem(props) && classes.buttonItem, + disableHighlight ? undefined : classes.highlightable, ), }; + // If submenu prop is true, return ItemWithSubmenu + if (hasSubMenu) { + return ( + + {children} + + ); + } + if (isButtonItem(props)) { return ( + {hasDropDown && showDropDown && ( +
+ {dropdownItems.map((object, key) => ( + + {object.title} + + ))} +
+ )} +
+ ); + } + + return ( +
+ + + + {title} + + +
+ ); +}; diff --git a/packages/core-components/src/layout/Sidebar/config.ts b/packages/core-components/src/layout/Sidebar/config.ts index d2a690e38f..e7a9e26e1d 100644 --- a/packages/core-components/src/layout/Sidebar/config.ts +++ b/packages/core-components/src/layout/Sidebar/config.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { createContext } from 'react'; +import { createContext, Dispatch, SetStateAction } from 'react'; const drawerWidthClosed = 72; const iconPadding = 24; @@ -37,13 +37,44 @@ export const sidebarConfig = { userBadgeDiameter: drawerWidthClosed - userBadgePadding * 2, }; +export const submenuConfig = { + drawerWidthClosed: 0, + drawerWidthOpen: 202, + // As per NN/g's guidance on timing for exposing hidden content + // See https://www.nngroup.com/articles/timing-exposing-content/ + defaultOpenDelayMs: 100, + defaultCloseDelayMs: 0, + defaultFadeDuration: 200, + logoHeight: 32, + iconContainerWidth: drawerWidthClosed, + iconSize: drawerWidthClosed - iconPadding * 2, + iconPadding, + selectedIndicatorWidth: 3, + userBadgePadding, + userBadgeDiameter: drawerWidthClosed - userBadgePadding * 2, +}; + export const SIDEBAR_INTRO_LOCAL_STORAGE = '@backstage/core/sidebar-intro-dismissed'; export type SidebarContextType = { isOpen: boolean; + setIsOpen: Dispatch>; }; export const SidebarContext = createContext({ isOpen: false, + setIsOpen: () => {}, }); + +export type ItemWithSubmenuContextType = { + isHoveredOn: boolean; + setIsHoveredOn: Dispatch>; +}; + +export const ItemWithSubmenuContext = createContext( + { + isHoveredOn: false, + setIsHoveredOn: () => {}, + }, +); diff --git a/packages/core-components/src/layout/Sidebar/icons/APIsIcon.tsx b/packages/core-components/src/layout/Sidebar/icons/APIsIcon.tsx new file mode 100644 index 0000000000..a185ab346d --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/icons/APIsIcon.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2021 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 React from 'react'; + +export const APIsIcon = () => { + return ( + + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx b/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx new file mode 100644 index 0000000000..d42a3ea957 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx @@ -0,0 +1,40 @@ +/* + * Copyright 2021 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 { SvgIcon } from '@material-ui/core'; +import React from 'react'; + +export const CatalogSidebarLogo = () => { + return ( + + + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/icons/DoubleArrowLeft.tsx b/packages/core-components/src/layout/Sidebar/icons/DoubleArrowLeft.tsx new file mode 100644 index 0000000000..ec1dbcdd31 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/icons/DoubleArrowLeft.tsx @@ -0,0 +1,50 @@ +/* + * 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 React from 'react'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles({ + svg: { + width: 'auto', + height: 15, + }, + path: { + fill: '#7df3e1', + }, +}); +const DoubleArrowRight = () => { + const classes = useStyles(); + + return ( + + + + + ); +}; + +export default DoubleArrowRight; diff --git a/packages/core-components/src/layout/Sidebar/icons/DoubleArrowRight.tsx b/packages/core-components/src/layout/Sidebar/icons/DoubleArrowRight.tsx new file mode 100644 index 0000000000..00d595175d --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/icons/DoubleArrowRight.tsx @@ -0,0 +1,50 @@ +/* + * 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 React from 'react'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles({ + svg: { + width: 'auto', + height: 15, + }, + path: { + fill: '#7df3e1', + }, +}); +const DoubleArrowRight = () => { + const classes = useStyles(); + + return ( + + + + + ); +}; + +export default DoubleArrowRight; diff --git a/packages/core-components/src/layout/Sidebar/icons/MiscIcon.tsx b/packages/core-components/src/layout/Sidebar/icons/MiscIcon.tsx new file mode 100644 index 0000000000..67010baeb5 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/icons/MiscIcon.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2021 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 React from 'react'; + +export const MiscIcon = () => { + return ( + + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/icons/ServicesIcon.tsx b/packages/core-components/src/layout/Sidebar/icons/ServicesIcon.tsx new file mode 100644 index 0000000000..49a619d422 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/icons/ServicesIcon.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2021 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 React from 'react'; + +export const ServicesIcon = () => { + return ( + + + + ); +}; diff --git a/packages/core-components/src/layout/Sidebar/index.ts b/packages/core-components/src/layout/Sidebar/index.ts index 067a9bf2eb..f25f7355f7 100644 --- a/packages/core-components/src/layout/Sidebar/index.ts +++ b/packages/core-components/src/layout/Sidebar/index.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -export { Sidebar } from './Bar'; +export { Sidebar, SidebarExpandButton } from './Bar'; +export { SubmenuItem } from './SubmenuItem'; export type { SidebarClassKey } from './Bar'; export { SidebarPage, SidebarPinStateContext } from './Page'; export type { SidebarPinStateContextType, SidebarPageClassKey } from './Page'; diff --git a/packages/theme/src/themes.ts b/packages/theme/src/themes.ts index d7a4a6d785..da5925b237 100644 --- a/packages/theme/src/themes.ts +++ b/packages/theme/src/themes.ts @@ -70,6 +70,15 @@ export const lightTheme = createTheme({ indicator: '#9BF0E1', color: '#b5b5b5', selectedColor: '#FFF', + navItem: { + hoverBackground: '#404040', + }, + submenu: { + background: '#404040', + indicator: '#9BF0E1', + color: '#b5b5b5', + selectedColor: '#FFF', + }, }, pinSidebarButton: { icon: '#181818', @@ -139,6 +148,15 @@ export const darkTheme = createTheme({ indicator: '#9BF0E1', color: '#b5b5b5', selectedColor: '#FFF', + navItem: { + hoverBackground: '#404040', + }, + submenu: { + background: '#404040', + indicator: '#9BF0E1', + color: '#b5b5b5', + selectedColor: '#FFF', + }, }, pinSidebarButton: { icon: '#404040', diff --git a/packages/theme/src/types.ts b/packages/theme/src/types.ts index 76a1b7eb5c..0b1217f00f 100644 --- a/packages/theme/src/types.ts +++ b/packages/theme/src/types.ts @@ -48,6 +48,15 @@ type PaletteAdditions = { indicator: string; color: string; selectedColor: string; + navItem: { + hoverBackground: string; + }; + submenu: { + background: string; + indicator: string; + color: string; + selectedColor: string; + }; }; tabbar: { indicator: string; From a35b6459d50b3979509365c532a51932f7c53ca9 Mon Sep 17 00:00:00 2001 From: Marley Powell Date: Thu, 21 Oct 2021 21:53:51 +0100 Subject: [PATCH 002/975] feat: Created `AzurePullRequestsIcon` component. Signed-off-by: Marley Powell --- .../AzurePullRequestsIcon.tsx | 28 +++++++++++++++++++ .../components/AzurePullRequestsIcon/index.ts | 17 +++++++++++ 2 files changed, 45 insertions(+) create mode 100644 plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx create mode 100644 plugins/azure-devops/src/components/AzurePullRequestsIcon/index.ts diff --git a/plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx b/plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx new file mode 100644 index 0000000000..3159556788 --- /dev/null +++ b/plugins/azure-devops/src/components/AzurePullRequestsIcon/AzurePullRequestsIcon.tsx @@ -0,0 +1,28 @@ +/* + * Copyright 2021 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 { SvgIcon, SvgIconProps } from '@material-ui/core'; + +import React from 'react'; + +export const AzurePullRequestsIcon = (props: SvgIconProps) => ( + + + +); diff --git a/plugins/azure-devops/src/components/AzurePullRequestsIcon/index.ts b/plugins/azure-devops/src/components/AzurePullRequestsIcon/index.ts new file mode 100644 index 0000000000..1f49119b4b --- /dev/null +++ b/plugins/azure-devops/src/components/AzurePullRequestsIcon/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 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. + */ + +export { AzurePullRequestsIcon } from './AzurePullRequestsIcon'; From 582a7964b7849681adb5bf8b4e0357d84a075a31 Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Thu, 28 Oct 2021 10:02:09 +0100 Subject: [PATCH 003/975] Add tests for scalable sidebar Signed-off-by: hiba-aldalaty --- .../src/layout/Sidebar/Bar.test.tsx | 104 ++++++++++++++++++ .../src/layout/Sidebar/Bar.tsx | 7 +- .../src/layout/Sidebar/Items.tsx | 1 + .../src/layout/Sidebar/Submenu.tsx | 3 +- .../src/layout/Sidebar/SubmenuItem.tsx | 4 +- 5 files changed, 116 insertions(+), 3 deletions(-) create mode 100644 packages/core-components/src/layout/Sidebar/Bar.test.tsx diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx new file mode 100644 index 0000000000..8a28d69e27 --- /dev/null +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -0,0 +1,104 @@ +/* + * 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 React from 'react'; +import { renderInTestApp } from '@backstage/test-utils'; +import { createEvent, fireEvent, screen, within } 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 { CatalogSidebarLogo } from './icons/CatalogSidebarLogo'; +import { MiscIcon } from './icons/MiscIcon'; +import { Sidebar, SidebarExpandButton } from './Bar'; +import { SidebarItem, SidebarSearchField } from './Items'; +import { hexToRgb } from '@material-ui/core/styles'; +import { SubmenuItem } from './SubmenuItem'; + +async function renderScalableSidebar() { + await renderInTestApp( + + {}} to="/search" /> + {}} + text="Catalog" + hasSubMenu + submenuTitle="Catalog" + > + + + + + + , + ); +} + +describe('Sidebar', () => { + beforeEach(async () => { + await renderScalableSidebar(); + }); + + describe('Click to Expand', () => { + it('Sidebar should show expanded items when expand button is clicked', async () => { + userEvent.click(screen.getByTestId('sidebar-expand-button')); + expect(await screen.findByText('Create...')).toBeInTheDocument(); + }); + }); + describe('Submenu Items', () => { + it('Extended sidebar with submenu content hidden by default', async () => { + expect(await screen.queryByText('Tools')).not.toBeInTheDocument(); + expect(await screen.queryByText('Misc')).not.toBeInTheDocument(); + }); + + it('Extended sidebar with submenu content visible when hover over submenu items', async () => { + userEvent.hover(screen.getByTestId('item-with-submenu')); + expect(await screen.findByText('Tools')).toBeInTheDocument(); + expect(await screen.findByText('Misc')).toBeInTheDocument(); + }); + + it('Multicategory item in submenu shows drop down on click', async () => { + userEvent.hover(screen.getByTestId('item-with-submenu')); + userEvent.click(screen.getByText('Misc')); + expect(await screen.getByText('dropdown item 1')).toBeInTheDocument(); + expect(await screen.getByText('dropdown item 2')).toBeInTheDocument(); + }); + + it('Dropdown item in submenu renders a link when `to` value is provided', async () => { + userEvent.hover(screen.getByTestId('item-with-submenu')); + userEvent.click(screen.getByText('Misc')); + expect(screen.getByText('dropdown item 1').closest('a')).toHaveAttribute( + 'href', + '/dropdownitemlink', + ); + }); + }); +}); diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 6fc2ac1cdb..23a4ae1286 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -146,7 +146,12 @@ export const SidebarExpandButton = () => { } return ( - All your software catalog entities diff --git a/yarn.lock b/yarn.lock index 517d66ba0c..fc66a02031 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4416,6 +4416,16 @@ react-is "^16.8.0 || ^17.0.0" react-transition-group "^4.4.0" +"@material-ui/data-grid@^4.0.0-alpha.37": + version "4.0.0-alpha.37" + resolved "https://registry.npmjs.org/@material-ui/data-grid/-/data-grid-4.0.0-alpha.37.tgz#89d907c4e94e6a0db4e89e4f59160f7811546ca2" + integrity sha512-3T2AG31aad/lWLMLwn1XUP4mUf3H9YZES17dGuYByzkRLCXbBZHBTPEnCctWukajzwm+v0KGg3QpwitGoiDAjA== + dependencies: + "@material-ui/utils" "^5.0.0-alpha.14" + clsx "^1.0.4" + prop-types "^15.7.2" + reselect "^4.0.0" + "@material-ui/icons@^4.11.2", "@material-ui/icons@^4.9.1": version "4.11.2" resolved "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.2.tgz#b3a7353266519cd743b6461ae9fdfcb1b25eb4c5" @@ -4503,6 +4513,17 @@ prop-types "^15.7.2" react-is "^16.8.0 || ^17.0.0" +"@material-ui/utils@^5.0.0-alpha.14": + version "5.0.0-beta.5" + resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-5.0.0-beta.5.tgz#de492037e1f1f0910fda32e6f11b66dfcde2a1c2" + integrity sha512-wtJ3ovXWZdTAz5eLBqvMpYH/IBJb3qMQbGCyL1i00+sf7AUlAuv4QLx+QtX/siA6L7IpxUQVfqpoCpQH1eYRpQ== + dependencies: + "@babel/runtime" "^7.14.8" + "@types/prop-types" "^15.7.4" + "@types/react-is" "^16.7.1 || ^17.0.0" + prop-types "^15.7.2" + react-is "^17.0.2" + "@mattiasbuelens/web-streams-polyfill@^0.2.0": version "0.2.1" resolved "https://registry.npmjs.org/@mattiasbuelens/web-streams-polyfill/-/web-streams-polyfill-0.2.1.tgz#d7c4aa94f98084ec0787be084d47167d62ea5f67" @@ -7434,7 +7455,7 @@ resolved "https://registry.npmjs.org/@types/pretty-hrtime/-/pretty-hrtime-1.0.1.tgz#72a26101dc567b0d68fd956cf42314556e42d601" integrity sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ== -"@types/prop-types@*", "@types/prop-types@^15.7.3": +"@types/prop-types@*", "@types/prop-types@^15.7.3", "@types/prop-types@^15.7.4": version "15.7.4" resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11" integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ== @@ -7475,6 +7496,13 @@ dependencies: "@types/react" "*" +"@types/react-is@^16.7.1 || ^17.0.0": + version "17.0.3" + resolved "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a" + integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw== + dependencies: + "@types/react" "*" + "@types/react-lazylog@^4.5.0": version "4.5.1" resolved "https://registry.npmjs.org/@types/react-lazylog/-/react-lazylog-4.5.1.tgz#babb5d814f7035b5434518769975e12f299356a8" From 741bcb168e875e537d7851f32195d9d30102d806 Mon Sep 17 00:00:00 2001 From: Jeremy Guarini Date: Fri, 29 Oct 2021 16:55:03 -0700 Subject: [PATCH 010/975] add changeset Signed-off-by: Jeremy Guarini --- .changeset/eight-months-agree.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .changeset/eight-months-agree.md diff --git a/.changeset/eight-months-agree.md b/.changeset/eight-months-agree.md new file mode 100644 index 0000000000..372c0b624c --- /dev/null +++ b/.changeset/eight-months-agree.md @@ -0,0 +1,19 @@ +--- +'@backstage/plugin-gcp-projects': patch +--- + +UI updates to GCP-projects plugin + +Adds the following to the project list page: + +- pagination +- filtering +- sorting +- rows per page +- show/hide columns + +Makes breadcrumb a link back to project list for the project details and new project views. + +In project list page, updates New project button to use RouterLink instead of href to avoid login prompt. + +In project details view, links to project details and logs now work, clicking on these will open the project or logs in GCP in new tab. From 044c38e73982d0793686b203d9dd356397c9a544 Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Mon, 1 Nov 2021 13:10:54 +0100 Subject: [PATCH 011/975] Lazy load all API definition widgets Signed-off-by: Oliver Sand --- .changeset/swift-turtles-provide.md | 7 + plugins/api-docs/api-report.md | 18 ++- ...t.test.tsx => AsyncApiDefinition.test.tsx} | 8 +- .../AsyncApiDefinition.tsx | 145 ++++++++++++++++++ .../AsyncApiDefinitionWidget.tsx | 137 ++--------------- ...et.test.tsx => GraphQlDefinition.test.tsx} | 6 +- .../GraphQlDefinition.tsx | 63 ++++++++ .../GraphQlDefinitionWidget.tsx | 55 ++----- ...et.test.tsx => OpenApiDefinition.test.tsx} | 8 +- .../OpenApiDefinition.tsx | 92 +++++++++++ .../OpenApiDefinitionWidget.tsx | 86 ++--------- 11 files changed, 375 insertions(+), 250 deletions(-) create mode 100644 .changeset/swift-turtles-provide.md rename plugins/api-docs/src/components/AsyncApiDefinitionWidget/{AsyncApiDefinitionWidget.test.tsx => AsyncApiDefinition.test.tsx} (87%) create mode 100644 plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx rename plugins/api-docs/src/components/GraphQlDefinitionWidget/{GraphQlDefinitionWidget.test.tsx => GraphQlDefinition.test.tsx} (89%) create mode 100644 plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx rename plugins/api-docs/src/components/OpenApiDefinitionWidget/{OpenApiDefinitionWidget.test.tsx => OpenApiDefinition.test.tsx} (87%) create mode 100644 plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx diff --git a/.changeset/swift-turtles-provide.md b/.changeset/swift-turtles-provide.md new file mode 100644 index 0000000000..52cf9a7c8f --- /dev/null +++ b/.changeset/swift-turtles-provide.md @@ -0,0 +1,7 @@ +--- +'@backstage/plugin-api-docs': patch +--- + +Lazy load all API definition widgets. The widgets use libraries like +`swagger-ui`, `graphiql`, and `@asyncapi/react-component` which are quite heavy +weight. To improve initial load times, the widgets are only loaded once used. diff --git a/plugins/api-docs/api-report.md b/plugins/api-docs/api-report.md index de36857945..8ec6175a6d 100644 --- a/plugins/api-docs/api-report.md +++ b/plugins/api-docs/api-report.md @@ -84,11 +84,13 @@ export const ApiTypeTitle: ({ apiEntity: ApiEntity; }) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "AsyncApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "AsyncApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const AsyncApiDefinitionWidget: ({ definition }: Props_5) => JSX.Element; +export const AsyncApiDefinitionWidget: ({ + definition, +}: AsyncApiDefinitionWidgetProps) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ConsumedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -100,7 +102,7 @@ export const ConsumedApisCard: ({ variant }: Props_2) => JSX.Element; // Warning: (ae-missing-release-tag) "ConsumingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ConsumingComponentsCard: ({ variant }: Props_6) => JSX.Element; +export const ConsumingComponentsCard: ({ variant }: Props_5) => JSX.Element; // Warning: (ae-missing-release-tag) "defaultDefinitionWidgets" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -169,11 +171,13 @@ export const EntityProvidingComponentsCard: ({ // @public (undocumented) export const HasApisCard: ({ variant }: Props_3) => JSX.Element; -// Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts +// Warning: (ae-forgotten-export) The symbol "OpenApiDefinitionWidgetProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "OpenApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element; +export const OpenApiDefinitionWidget: ({ + definition, +}: OpenApiDefinitionWidgetProps) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "PlainApiDefinitionWidget" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -182,7 +186,7 @@ export const OpenApiDefinitionWidget: ({ definition }: Props_8) => JSX.Element; export const PlainApiDefinitionWidget: ({ definition, language, -}: Props_9) => JSX.Element; +}: Props_7) => JSX.Element; // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "ProvidedApisCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -194,5 +198,5 @@ export const ProvidedApisCard: ({ variant }: Props_4) => JSX.Element; // Warning: (ae-missing-release-tag) "ProvidingComponentsCard" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public (undocumented) -export const ProvidingComponentsCard: ({ variant }: Props_7) => JSX.Element; +export const ProvidingComponentsCard: ({ variant }: Props_6) => JSX.Element; ``` diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx similarity index 87% rename from plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx rename to plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx index 0170ada180..cb89476c03 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.test.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.test.tsx @@ -16,9 +16,9 @@ import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { AsyncApiDefinitionWidget } from './AsyncApiDefinitionWidget'; +import { AsyncApiDefinition } from './AsyncApiDefinition'; -describe('', () => { +describe('', () => { it('renders asyncapi spec', async () => { const definition = ` asyncapi: 2.0.0 @@ -40,7 +40,7 @@ components: type: string `; const { getByText, getAllByText } = await renderInTestApp( - , + , ); expect(getByText(/Account Service/i)).toBeInTheDocument(); @@ -51,7 +51,7 @@ components: it('renders error if definition is missing', async () => { const { getByText } = await renderInTestApp( - , + , ); expect(getByText(/Error/i)).toBeInTheDocument(); expect(getByText(/Document can't be null or falsey/i)).toBeInTheDocument(); diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx new file mode 100644 index 0000000000..1c993f67d7 --- /dev/null +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinition.tsx @@ -0,0 +1,145 @@ +/* + * 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 AsyncApi from '@asyncapi/react-component'; +import '@asyncapi/react-component/lib/styles/fiori.css'; +import { alpha, makeStyles } from '@material-ui/core/styles'; +import React from 'react'; + +const useStyles = makeStyles(theme => ({ + root: { + '& .asyncapi': { + 'font-family': 'inherit', + background: 'none', + }, + '& h2': { + ...theme.typography.h6, + }, + '& .text-teal': { + color: theme.palette.primary.main, + }, + '& button': { + ...theme.typography.button, + background: 'none', + boxSizing: 'border-box', + minWidth: 64, + borderRadius: theme.shape.borderRadius, + transition: theme.transitions.create( + ['background-color', 'box-shadow', 'border'], + { + duration: theme.transitions.duration.short, + }, + ), + padding: '5px 15px', + color: theme.palette.primary.main, + border: `1px solid ${alpha(theme.palette.primary.main, 0.5)}`, + '&:hover': { + textDecoration: 'none', + '&.Mui-disabled': { + backgroundColor: 'transparent', + }, + border: `1px solid ${theme.palette.primary.main}`, + backgroundColor: alpha( + theme.palette.primary.main, + theme.palette.action.hoverOpacity, + ), + // Reset on touch devices, it doesn't add specificity + '@media (hover: none)': { + backgroundColor: 'transparent', + }, + }, + '&.Mui-disabled': { + color: theme.palette.action.disabled, + }, + }, + '& .asyncapi__collapse-button:hover': { + color: theme.palette.primary.main, + }, + '& button.asyncapi__toggle-button': { + 'min-width': 'inherit', + }, + '& .asyncapi__info-list li': { + 'border-color': theme.palette.primary.main, + '&:hover': { + color: theme.palette.text.primary, + 'border-color': theme.palette.primary.main, + 'background-color': theme.palette.primary.main, + }, + }, + '& .asyncapi__info-list li a': { + color: theme.palette.primary.main, + '&:hover': { + color: theme.palette.getContrastText(theme.palette.primary.main), + }, + }, + '& .asyncapi__enum': { + color: theme.palette.secondary.main, + }, + '& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div': + { + 'background-color': 'inherit', + }, + '& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4': + { + 'background-color': 'inherit', + color: theme.palette.text.primary, + }, + '& .asyncapi__additional-properties-notice': { + color: theme.palette.text.hint, + }, + '& .asyncapi__code, .asyncapi__code-pre': { + background: theme.palette.background.default, + }, + '& .asyncapi__schema-example-header-title': { + color: theme.palette.text.secondary, + }, + '& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header': + { + 'background-color': 'inherit', + color: theme.palette.text.secondary, + }, + '& .asyncapi__table-header': { + background: theme.palette.background.default, + }, + '& .asyncapi__table-body': { + color: theme.palette.text.primary, + }, + '& .asyncapi__server-security-flow': { + background: theme.palette.background.default, + border: 'none', + }, + '& .asyncapi__server-security-flows-list a': { + color: theme.palette.primary.main, + }, + '& .asyncapi__table-row--nested': { + color: theme.palette.text.secondary, + }, + }, +})); + +type Props = { + definition: string; +}; + +export const AsyncApiDefinition = ({ definition }: Props) => { + const classes = useStyles(); + + return ( +
+ +
+ ); +}; diff --git a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx index f32b5d01b5..a3d9bc1f64 100644 --- a/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/AsyncApiDefinitionWidget/AsyncApiDefinitionWidget.tsx @@ -14,132 +14,27 @@ * limitations under the License. */ -import AsyncApi from '@asyncapi/react-component'; -import '@asyncapi/react-component/lib/styles/fiori.css'; -import { fade, makeStyles } from '@material-ui/core/styles'; -import React from 'react'; +import { Progress } from '@backstage/core-components'; +import React, { Suspense } from 'react'; -const useStyles = makeStyles(theme => ({ - root: { - '& .asyncapi': { - 'font-family': 'inherit', - background: 'none', - }, - '& h2': { - ...theme.typography.h6, - }, - '& .text-teal': { - color: theme.palette.primary.main, - }, - '& button': { - ...theme.typography.button, - background: 'none', - boxSizing: 'border-box', - minWidth: 64, - borderRadius: theme.shape.borderRadius, - transition: theme.transitions.create( - ['background-color', 'box-shadow', 'border'], - { - duration: theme.transitions.duration.short, - }, - ), - padding: '5px 15px', - color: theme.palette.primary.main, - border: `1px solid ${fade(theme.palette.primary.main, 0.5)}`, - '&:hover': { - textDecoration: 'none', - '&.Mui-disabled': { - backgroundColor: 'transparent', - }, - border: `1px solid ${theme.palette.primary.main}`, - backgroundColor: fade( - theme.palette.primary.main, - theme.palette.action.hoverOpacity, - ), - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - backgroundColor: 'transparent', - }, - }, - '&.Mui-disabled': { - color: theme.palette.action.disabled, - }, - }, - '& .asyncapi__collapse-button:hover': { - color: theme.palette.primary.main, - }, - '& button.asyncapi__toggle-button': { - 'min-width': 'inherit', - }, - '& .asyncapi__info-list li': { - 'border-color': theme.palette.primary.main, - '&:hover': { - color: theme.palette.text.primary, - 'border-color': theme.palette.primary.main, - 'background-color': theme.palette.primary.main, - }, - }, - '& .asyncapi__info-list li a': { - color: theme.palette.primary.main, - '&:hover': { - color: theme.palette.getContrastText(theme.palette.primary.main), - }, - }, - '& .asyncapi__enum': { - color: theme.palette.secondary.main, - }, - '& .asyncapi__info, .asyncapi__channel, .asyncapi__channels > div, .asyncapi__schema, .asyncapi__channel-operations-list .asyncapi__messages-list-item .asyncapi__message, .asyncapi__message, .asyncapi__server, .asyncapi__servers > div, .asyncapi__messages > div, .asyncapi__schemas > div': - { - 'background-color': 'inherit', - }, - '& .asyncapi__channel-parameters-header, .asyncapi__channel-operations-header, .asyncapi__channel-operation-oneOf-subscribe-header, .asyncapi__channel-operation-oneOf-publish-header, .asyncapi__channel-operation-message-header, .asyncapi__message-header, .asyncapi__message-header-title, .asyncapi__message-header-title > h3, .asyncapi__bindings, .asyncapi__bindings-header, .asyncapi__bindings-header > h4': - { - 'background-color': 'inherit', - color: theme.palette.text.primary, - }, - '& .asyncapi__additional-properties-notice': { - color: theme.palette.text.hint, - }, - '& .asyncapi__code, .asyncapi__code-pre': { - background: theme.palette.background.default, - }, - '& .asyncapi__schema-example-header-title': { - color: theme.palette.text.secondary, - }, - '& .asyncapi__message-headers-header, .asyncapi__message-payload-header, .asyncapi__server-variables-header, .asyncapi__server-security-header': - { - 'background-color': 'inherit', - color: theme.palette.text.secondary, - }, - '& .asyncapi__table-header': { - background: theme.palette.background.default, - }, - '& .asyncapi__table-body': { - color: theme.palette.text.primary, - }, - '& .asyncapi__server-security-flow': { - background: theme.palette.background.default, - border: 'none', - }, - '& .asyncapi__server-security-flows-list a': { - color: theme.palette.primary.main, - }, - '& .asyncapi__table-row--nested': { - color: theme.palette.text.secondary, - }, - }, -})); +// The asyncapi component and related CSS has a significant size, only load it +// if the element is actually used. +const LazyAsyncApiDefinition = React.lazy(() => + import('./AsyncApiDefinition').then(m => ({ + default: m.AsyncApiDefinition, + })), +); -type Props = { +export type AsyncApiDefinitionWidgetProps = { definition: string; }; -export const AsyncApiDefinitionWidget = ({ definition }: Props) => { - const classes = useStyles(); - +export const AsyncApiDefinitionWidget = ({ + definition, +}: AsyncApiDefinitionWidgetProps) => { return ( -
- -
+ }> + + ); }; diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx similarity index 89% rename from plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx rename to plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx index 63e5a73692..fd78fa9204 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.test.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.test.tsx @@ -16,9 +16,9 @@ import { renderInTestApp } from '@backstage/test-utils'; import React from 'react'; -import { GraphQlDefinitionWidget } from './GraphQlDefinitionWidget'; +import { GraphQlDefinition } from './GraphQlDefinition'; -describe('', () => { +describe('', () => { it('renders graphql schema', async () => { const definition = ` """Hello World!""" @@ -53,7 +53,7 @@ type Film { }; const { getByText } = await renderInTestApp( - , + , ); expect(getByText(/Film/i)).toBeInTheDocument(); diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx new file mode 100644 index 0000000000..d3fddf70cf --- /dev/null +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinition.tsx @@ -0,0 +1,63 @@ +/* + * 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 { BackstageTheme } from '@backstage/theme'; +import { makeStyles } from '@material-ui/core/styles'; +import GraphiQL from 'graphiql'; +import 'graphiql/graphiql.css'; +import { buildSchema } from 'graphql'; +import React from 'react'; + +const useStyles = makeStyles(() => ({ + root: { + height: '100%', + display: 'flex', + flexFlow: 'column nowrap', + }, + graphiQlWrapper: { + flex: 1, + '@global': { + '.graphiql-container': { + boxSizing: 'initial', + height: '100%', + minHeight: '600px', + flex: '1 1 auto', + }, + }, + }, +})); + +type Props = { + definition: string; +}; + +export const GraphQlDefinition = ({ definition }: Props) => { + const classes = useStyles(); + const schema = buildSchema(definition); + + return ( +
+
+ Promise.resolve(null) as any} + schema={schema} + docExplorerOpen + defaultSecondaryEditorOpen={false} + /> +
+
+ ); +}; diff --git a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx index 8d15ba08a2..e91387bffa 100644 --- a/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/GraphQlDefinitionWidget/GraphQlDefinitionWidget.tsx @@ -14,54 +14,27 @@ * limitations under the License. */ -import { BackstageTheme } from '@backstage/theme'; -import { makeStyles } from '@material-ui/core/styles'; -import 'graphiql/graphiql.css'; -import { buildSchema } from 'graphql'; -import React, { Suspense } from 'react'; import { Progress } from '@backstage/core-components'; +import React, { Suspense } from 'react'; -const GraphiQL = React.lazy(() => import('graphiql')); +// The graphql component, graphql and related CSS has a significant size, only +// load it if the element is actually used. +const LazyGraphQlDefinition = React.lazy(() => + import('./GraphQlDefinition').then(m => ({ + default: m.GraphQlDefinition, + })), +); -const useStyles = makeStyles(() => ({ - root: { - height: '100%', - display: 'flex', - flexFlow: 'column nowrap', - }, - graphiQlWrapper: { - flex: 1, - '@global': { - '.graphiql-container': { - boxSizing: 'initial', - height: '100%', - minHeight: '600px', - flex: '1 1 auto', - }, - }, - }, -})); - -type Props = { - definition: any; +export type GraphQlDefinitionWidgetProps = { + definition: string; }; -export const GraphQlDefinitionWidget = ({ definition }: Props) => { - const classes = useStyles(); - const schema = buildSchema(definition); - +export const GraphQlDefinitionWidget = ({ + definition, +}: GraphQlDefinitionWidgetProps) => { return ( }> -
-
- Promise.resolve(null) as any} - schema={schema} - docExplorerOpen - defaultSecondaryEditorOpen={false} - /> -
-
+
); }; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx similarity index 87% rename from plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx rename to plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx index 080aa11a3a..735fba86f7 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.test.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.test.tsx @@ -17,9 +17,9 @@ import { renderInTestApp } from '@backstage/test-utils'; import { waitFor } from '@testing-library/react'; import React from 'react'; -import { OpenApiDefinitionWidget } from './OpenApiDefinitionWidget'; +import { OpenApiDefinition } from './OpenApiDefinition'; -describe('', () => { +describe('', () => { it('renders openapi spec', async () => { const definition = ` openapi: "3.0.0" @@ -39,7 +39,7 @@ paths: description: Success `; const { getByText } = await renderInTestApp( - , + , ); // swagger-ui loads the documentation asynchronously @@ -51,7 +51,7 @@ paths: it('renders error if definition is missing', async () => { const { getByText } = await renderInTestApp( - , + , ); expect(getByText(/No API definition provided/i)).toBeInTheDocument(); }); diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx new file mode 100644 index 0000000000..cc3df40ae5 --- /dev/null +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinition.tsx @@ -0,0 +1,92 @@ +/* + * 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 { makeStyles } from '@material-ui/core/styles'; +import React, { useEffect, useState } from 'react'; +import SwaggerUI from 'swagger-ui-react'; +import 'swagger-ui-react/swagger-ui.css'; + +const useStyles = makeStyles(theme => ({ + root: { + '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': { + 'font-family': 'inherit', + color: theme.palette.text.primary, + }, + '& .scheme-container': { + 'background-color': theme.palette.background.default, + }, + '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th': + { + color: theme.palette.text.primary, + 'border-color': theme.palette.divider, + }, + '& section.models, section.models.is-open h4': { + 'border-color': theme.palette.divider, + }, + '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3': + { + color: theme.palette.text.secondary, + }, + '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row': + { + color: theme.palette.text.primary, + }, + '& .opblock .opblock-section-header, .model-box, section.models .model-container': + { + background: theme.palette.background.default, + }, + '& .prop-format, .parameter__in': { + color: theme.palette.text.disabled, + }, + '& ': { + color: theme.palette.text.primary, + 'border-color': theme.palette.divider, + }, + '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model': + { + color: theme.palette.text.hint, + }, + '& .parameter__name.required:after': { + color: theme.palette.warning.dark, + }, + '& .prop-type': { + color: theme.palette.primary.main, + }, + }, +})); + +type Props = { + definition: string; +}; + +export const OpenApiDefinition = ({ definition }: Props) => { + const classes = useStyles(); + + // Due to a bug in the swagger-ui-react component, the component needs + // to be created without content first. + const [def, setDef] = useState(''); + + useEffect(() => { + const timer = setTimeout(() => setDef(definition), 0); + return () => clearTimeout(timer); + }, [definition, setDef]); + + return ( +
+ +
+ ); +}; diff --git a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx index 4c22d29bc2..2c223243d1 100644 --- a/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx +++ b/plugins/api-docs/src/components/OpenApiDefinitionWidget/OpenApiDefinitionWidget.tsx @@ -14,81 +14,27 @@ * limitations under the License. */ -import { makeStyles } from '@material-ui/core/styles'; -import React, { useEffect, useState } from 'react'; -import SwaggerUI from 'swagger-ui-react'; -import 'swagger-ui-react/swagger-ui.css'; +import { Progress } from '@backstage/core-components'; +import React, { Suspense } from 'react'; -// TODO: Schemas +// The swagger-ui component and related CSS has a significant size, only load it +// if the element is actually used. +const LazyOpenApiDefinition = React.lazy(() => + import('./OpenApiDefinition').then(m => ({ + default: m.OpenApiDefinition, + })), +); -const useStyles = makeStyles(theme => ({ - root: { - '& .swagger-ui, .info h1, .info h2, .info h3, .info h4, .info h': { - 'font-family': 'inherit', - color: theme.palette.text.primary, - }, - '& .scheme-container': { - 'background-color': theme.palette.background.default, - }, - '& .opblock-tag, .opblock-tag small, table thead tr td, table thead tr th': - { - color: theme.palette.text.primary, - 'border-color': theme.palette.divider, - }, - '& section.models, section.models.is-open h4': { - 'border-color': theme.palette.divider, - }, - '& .opblock .opblock-summary-description, .parameter__type, table.headers td, .model-title, .model .property.primitive, section h3': - { - color: theme.palette.text.secondary, - }, - '& .opblock .opblock-summary-operation-id, .opblock .opblock-summary-path, .opblock .opblock-summary-path__deprecated, .opblock .opblock-section-header h4, .parameter__name, .response-col_status, .response-col_links, .responses-inner h4, .swagger-ui .responses-inner h5, .opblock-section-header .btn, .tab li, .info li, .info p, .info table, section.models h4, .info .title, table.model tr.description, .property-row': - { - color: theme.palette.text.primary, - }, - '& .opblock .opblock-section-header, .model-box, section.models .model-container': - { - background: theme.palette.background.default, - }, - '& .prop-format, .parameter__in': { - color: theme.palette.text.disabled, - }, - '& ': { - color: theme.palette.text.primary, - 'border-color': theme.palette.divider, - }, - '& .opblock-description-wrapper p, .opblock-external-docs-wrapper p, .opblock-title_normal p, .response-control-media-type__accept-message, .opblock .opblock-section-header>label, .scheme-container .schemes>label, .info .base-url, .model': - { - color: theme.palette.text.hint, - }, - '& .parameter__name.required:after': { - color: theme.palette.warning.dark, - }, - '& .prop-type': { - color: theme.palette.primary.main, - }, - }, -})); - -type Props = { +export type OpenApiDefinitionWidgetProps = { definition: string; }; -export const OpenApiDefinitionWidget = ({ definition }: Props) => { - const classes = useStyles(); - - // Due to a bug in the swagger-ui-react component, the component needs - // to be created without content first. - const [def, setDef] = useState(''); - - useEffect(() => { - const timer = setTimeout(() => setDef(definition), 0); - return () => clearTimeout(timer); - }, [definition, setDef]); - +export const OpenApiDefinitionWidget = ({ + definition, +}: OpenApiDefinitionWidgetProps) => { return ( -
- -
+ }> + + ); }; From d396d8c7dc6fe8b476a391bca41ecd521c3f4812 Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Tue, 2 Nov 2021 09:20:48 +0000 Subject: [PATCH 012/975] Revert "Update api-report.md" This reverts commit cf43248673884b5b388f0d1d1d156fc523a72f69. Signed-off-by: hiba-aldalaty --- packages/core-components/api-report.md | 29 +++++--------------------- packages/theme/api-report.md | 9 -------- 2 files changed, 5 insertions(+), 33 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index ae7a781729..cc884c9e8f 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -16,7 +16,6 @@ import { ComponentProps } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; -import { Dispatch } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; @@ -34,7 +33,6 @@ import * as React_3 from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { SessionApi } from '@backstage/core-plugin-api'; -import { SetStateAction } from 'react'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; import { SparklinesProps } from 'react-sparklines'; @@ -102,7 +100,7 @@ export type BottomLinkProps = { // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function Breadcrumbs(props: Props_20): JSX.Element; +export function Breadcrumbs(props: Props_21): JSX.Element; // @public (undocumented) export type BreadcrumbsClickableTextClassKey = 'root'; @@ -769,10 +767,11 @@ 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 function Sidebar({ children }: PropsWithChildren<{}>): JSX.Element; +export function Sidebar(props: 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) // @@ -813,7 +812,6 @@ export const SidebarContext: Context; // @public (undocumented) export type SidebarContextType = { isOpen: boolean; - setIsOpen: Dispatch>; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1085,11 +1083,6 @@ export const SidebarDivider: React_2.ComponentType< } >; -// Warning: (ae-missing-release-tag) "SidebarExpandButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -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) @@ -1968,7 +1961,7 @@ export const SidebarSpacer: React_2.ComponentType< // 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_19): 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) // @@ -2069,18 +2062,6 @@ export type StructuredMetadataTableListClassKey = 'root'; // @public (undocumented) export type StructuredMetadataTableNestedListClassKey = 'root'; -// Warning: (ae-forgotten-export) The symbol "SubmenuItemProps" needs to be exported by the entry point index.d.ts -// Warning: (ae-missing-release-tag) "SubmenuItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) -// -// @public (undocumented) -export const SubmenuItem: ({ - title, - to, - hasDropDown, - icon: Icon, - dropdownItems, -}: SubmenuItemProps) => JSX.Element; - // Warning: (ae-forgotten-export) The symbol "SubvalueCellProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SubvalueCell" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2151,7 +2132,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/theme/api-report.md b/packages/theme/api-report.md index cc27dcb150..72d7887214 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -41,15 +41,6 @@ export type BackstagePaletteAdditions = { indicator: string; color: string; selectedColor: string; - navItem: { - hoverBackground: string; - }; - submenu: { - background: string; - indicator: string; - color: string; - selectedColor: string; - }; }; tabbar: { indicator: string; From 663463adbd4a024f15a5f96e97d94b575cc69fbd Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Tue, 2 Nov 2021 09:54:42 +0000 Subject: [PATCH 013/975] Reattempt building api-report.md for @backstage/core-components Signed-off-by: hiba-aldalaty --- packages/core-components/api-report.md | 29 +++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index cc884c9e8f..ae7a781729 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -16,6 +16,7 @@ import { ComponentProps } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; +import { Dispatch } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; @@ -33,6 +34,7 @@ import * as React_3 from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { SessionApi } from '@backstage/core-plugin-api'; +import { SetStateAction } from 'react'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; import { SparklinesProps } from 'react-sparklines'; @@ -100,7 +102,7 @@ export type BottomLinkProps = { // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function Breadcrumbs(props: Props_21): JSX.Element; +export function Breadcrumbs(props: Props_20): JSX.Element; // @public (undocumented) export type BreadcrumbsClickableTextClassKey = 'root'; @@ -767,11 +769,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 function Sidebar(props: PropsWithChildren): JSX.Element; +export function Sidebar({ children }: 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) // @@ -812,6 +813,7 @@ export const SidebarContext: Context; // @public (undocumented) export type SidebarContextType = { isOpen: boolean; + setIsOpen: Dispatch>; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1083,6 +1085,11 @@ export const SidebarDivider: React_2.ComponentType< } >; +// Warning: (ae-missing-release-tag) "SidebarExpandButton" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +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) @@ -1961,7 +1968,7 @@ export const SidebarSpacer: React_2.ComponentType< // 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_19): JSX.Element; +export function SignInPage(props: Props_18): 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) // @@ -2062,6 +2069,18 @@ export type StructuredMetadataTableListClassKey = 'root'; // @public (undocumented) export type StructuredMetadataTableNestedListClassKey = 'root'; +// Warning: (ae-forgotten-export) The symbol "SubmenuItemProps" needs to be exported by the entry point index.d.ts +// Warning: (ae-missing-release-tag) "SubmenuItem" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) +// +// @public (undocumented) +export const SubmenuItem: ({ + title, + to, + hasDropDown, + icon: Icon, + dropdownItems, +}: SubmenuItemProps) => JSX.Element; + // Warning: (ae-forgotten-export) The symbol "SubvalueCellProps" needs to be exported by the entry point index.d.ts // Warning: (ae-missing-release-tag) "SubvalueCell" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // @@ -2132,7 +2151,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) // From f0f2bfea6e2a085a26abccd4905dd42bb2130058 Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Tue, 2 Nov 2021 11:39:27 +0000 Subject: [PATCH 014/975] Reattempt building api-report.md for @backstage/theme Signed-off-by: hiba-aldalaty --- packages/theme/api-report.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index 30dcb7b040..cc27dcb150 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -41,6 +41,15 @@ export type BackstagePaletteAdditions = { indicator: string; color: string; selectedColor: string; + navItem: { + hoverBackground: string; + }; + submenu: { + background: string; + indicator: string; + color: string; + selectedColor: string; + }; }; tabbar: { indicator: string; @@ -64,7 +73,6 @@ export type BackstagePaletteAdditions = { error: string; text: string; link: string; - warning: string; }; }; From 4891a8f2e6f8e3c0993b6826676ea97bf0592895 Mon Sep 17 00:00:00 2001 From: Dede Hamzah Date: Wed, 3 Nov 2021 16:08:27 +0700 Subject: [PATCH 015/975] Add Props Icon for Sidebar Item SidebarSearchField and Settings Signed-off-by: Dede Hamzah --- .../src/layout/Sidebar/Items.tsx | 4 +++- .../user-settings/src/components/Settings.tsx | 17 +++++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/core-components/src/layout/Sidebar/Items.tsx b/packages/core-components/src/layout/Sidebar/Items.tsx index 883e0395cd..75317ed9ac 100644 --- a/packages/core-components/src/layout/Sidebar/Items.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.tsx @@ -293,11 +293,13 @@ export const SidebarItem = forwardRef((props, ref) => { type SidebarSearchFieldProps = { onSearch: (input: string) => void; to?: string; + icon?: IconComponent; }; export function SidebarSearchField(props: SidebarSearchFieldProps) { const [input, setInput] = useState(''); const classes = useStyles(); + const Icon = props.icon ? props.icon : SearchIcon; const search = () => { props.onSearch(input); @@ -329,7 +331,7 @@ export function SidebarSearchField(props: SidebarSearchFieldProps) { return (
- + { - return ( - - ); +type SettingsProps = { + icon?: IconComponent; +}; + +export const Settings = (props: SettingsProps) => { + const Icon = props.icon ? props.icon : SettingsIcon; + + return ; }; From 274a4fc6335eff6091e995254636c98b7650d79e Mon Sep 17 00:00:00 2001 From: Dede Hamzah Date: Wed, 3 Nov 2021 16:11:23 +0700 Subject: [PATCH 016/975] add changeset Signed-off-by: Dede Hamzah --- .changeset/khaki-rice-kick.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/khaki-rice-kick.md diff --git a/.changeset/khaki-rice-kick.md b/.changeset/khaki-rice-kick.md new file mode 100644 index 0000000000..54819b855a --- /dev/null +++ b/.changeset/khaki-rice-kick.md @@ -0,0 +1,6 @@ +--- +'@backstage/core-components': patch +'@backstage/plugin-user-settings': patch +--- + +Add Props Icon for Sidebar Item SidebarSearchField and Settings From ddf7325869f60b628b57cd8f22bda58c4303108f Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Tue, 2 Nov 2021 13:07:05 +0000 Subject: [PATCH 017/975] Update api-report.md for themes Signed-off-by: hiba-aldalaty --- packages/theme/api-report.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index cc27dcb150..6a85bafed6 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -73,6 +73,7 @@ export type BackstagePaletteAdditions = { error: string; text: string; link: string; + warning: string; }; }; From b90fc74d7099b815d8a22eab4f04bbf106a65b33 Mon Sep 17 00:00:00 2001 From: Bryce Larson Date: Thu, 4 Nov 2021 20:08:58 +1100 Subject: [PATCH 018/975] feat: Add getDefaultProcessors to CatalogBuilder Signed-off-by: Bryce Larson --- .changeset/violet-panthers-care.md | 5 +++ .../src/service/NextCatalogBuilder.ts | 38 +++++++++++++------ 2 files changed, 32 insertions(+), 11 deletions(-) create mode 100644 .changeset/violet-panthers-care.md diff --git a/.changeset/violet-panthers-care.md b/.changeset/violet-panthers-care.md new file mode 100644 index 0000000000..7a43c87f37 --- /dev/null +++ b/.changeset/violet-panthers-care.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-catalog-backend': patch +--- + +adds getDefaultProcessor method to CatalogBuilder diff --git a/plugins/catalog-backend/src/service/NextCatalogBuilder.ts b/plugins/catalog-backend/src/service/NextCatalogBuilder.ts index 835c1dc700..5e0bcd9a75 100644 --- a/plugins/catalog-backend/src/service/NextCatalogBuilder.ts +++ b/plugins/catalog-backend/src/service/NextCatalogBuilder.ts @@ -265,7 +265,8 @@ export class NextCatalogBuilder { * Sets what entity processors to use. These are responsible for reading, * parsing, and processing entities before they are persisted in the catalog. * - * This function replaces the default set of processors; use with care. + * This function replaces the default set of processors, consider using with + * {@link NextCatalogBuilder#getDefaultProcessors}; use with care. * * @param processors One or more processors */ @@ -275,6 +276,30 @@ export class NextCatalogBuilder { return this; } + /** + * Returns the default list of entity processors. These are responsible for reading, + * parsing, and processing entities before they are persisted in the catalog. Changing + * the order of processing can give more control to custom processors. + * + * Consider using with with {@link NextCatalogBuilder#replaceProcessors} + * + */ + getDefaultProcessors(): CatalogProcessor[] { + const { config, logger, reader } = this.env; + const integrations = ScmIntegrations.fromConfig(config); + + return [ + new FileReaderProcessor(), + BitbucketDiscoveryProcessor.fromConfig(config, { logger }), + GithubDiscoveryProcessor.fromConfig(config, { logger }), + GithubOrgReaderProcessor.fromConfig(config, { logger }), + GitLabDiscoveryProcessor.fromConfig(config, { logger }), + new UrlReaderProcessor({ reader, logger }), + CodeOwnersProcessor.fromConfig(config, { logger, reader }), + new AnnotateLocationEntityProcessor({ integrations }), + ]; + } + /** * Sets up the catalog to use a custom parser for entity data. * @@ -416,16 +441,7 @@ export class NextCatalogBuilder { // These are only added unless the user replaced them all if (!this.processorsReplace) { - processors.push( - new FileReaderProcessor(), - BitbucketDiscoveryProcessor.fromConfig(config, { logger }), - GithubDiscoveryProcessor.fromConfig(config, { logger }), - GithubOrgReaderProcessor.fromConfig(config, { logger }), - GitLabDiscoveryProcessor.fromConfig(config, { logger }), - new UrlReaderProcessor({ reader, logger }), - CodeOwnersProcessor.fromConfig(config, { logger, reader }), - new AnnotateLocationEntityProcessor({ integrations }), - ); + processors.push(...this.getDefaultProcessors()); } // Add the ones (if any) that the user added From a6cf7598a15f8a409bb3dd8d7cc65555b09b4363 Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Thu, 4 Nov 2021 13:59:55 +0000 Subject: [PATCH 019/975] Make sidebar expand on hover by default to prevent adopter impact Signed-off-by: hiba-aldalaty --- packages/app/src/components/Root/Root.tsx | 2 - .../src/layout/Sidebar/Bar.test.tsx | 6 +- .../src/layout/Sidebar/Bar.tsx | 103 +++++++++++++----- .../src/layout/Sidebar/Items.test.tsx | 2 +- .../src/layout/Sidebar/Sidebar.stories.tsx | 12 ++ .../src/layout/Sidebar/config.ts | 6 +- .../Sidebar/icons/CatalogSidebarLogo.tsx | 1 + packages/theme/src/themes.ts | 6 - packages/theme/src/types.ts | 3 - 9 files changed, 100 insertions(+), 41 deletions(-) diff --git a/packages/app/src/components/Root/Root.tsx b/packages/app/src/components/Root/Root.tsx index b0318cdaf8..7a666a020a 100644 --- a/packages/app/src/components/Root/Root.tsx +++ b/packages/app/src/components/Root/Root.tsx @@ -40,7 +40,6 @@ import { SidebarDivider, SidebarSpace, SidebarScrollWrapper, - SidebarExpandButton, } from '@backstage/core-components'; const useSidebarLogoStyles = makeStyles({ @@ -99,7 +98,6 @@ export const Root = ({ children }: PropsWithChildren<{}>) => ( - diff --git a/packages/core-components/src/layout/Sidebar/Bar.test.tsx b/packages/core-components/src/layout/Sidebar/Bar.test.tsx index 74a4fb468e..2039210c2d 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.test.tsx @@ -28,7 +28,7 @@ import { SubmenuItem } from './SubmenuItem'; async function renderScalableSidebar() { await renderInTestApp( - + {}} to="/search" /> { userEvent.click(screen.getByTestId('sidebar-expand-button')); expect(await screen.findByText('Create...')).toBeInTheDocument(); }); + it('Sidebar should not show expanded items when hovered on', async () => { + userEvent.hover(screen.getByTestId('sidebar-root')); + expect(await screen.queryByText('Create...')).not.toBeInTheDocument(); + }); }); describe('Submenu Items', () => { it('Extended sidebar with submenu content hidden by default', async () => { diff --git a/packages/core-components/src/layout/Sidebar/Bar.tsx b/packages/core-components/src/layout/Sidebar/Bar.tsx index 23a4ae1286..1fb8dc5fc6 100644 --- a/packages/core-components/src/layout/Sidebar/Bar.tsx +++ b/packages/core-components/src/layout/Sidebar/Bar.tsx @@ -17,12 +17,7 @@ import { makeStyles } from '@material-ui/core/styles'; import useMediaQuery from '@material-ui/core/useMediaQuery'; import clsx from 'clsx'; -import React, { - useState, - useContext, - PropsWithChildren, - useEffect, -} from 'react'; +import React, { useState, useContext, PropsWithChildren, useRef } from 'react'; import { sidebarConfig, SidebarContext } from './config'; import { BackstageTheme } from '@backstage/theme'; import { SidebarPinStateContext } from './Page'; @@ -88,24 +83,85 @@ const useStyles = makeStyles( { name: 'BackstageSidebar' }, ); -export function Sidebar({ children }: PropsWithChildren<{}>) { +enum State { + Closed, + Idle, + Open, +} + +type Props = { + openDelayMs?: number; + closeDelayMs?: number; + disableExpandOnHover?: boolean; +}; + +export function Sidebar(props: PropsWithChildren) { + const { + disableExpandOnHover = false, + openDelayMs = sidebarConfig.defaultOpenDelayMs, + closeDelayMs = sidebarConfig.defaultCloseDelayMs, + children, + } = props; const classes = useStyles(); - + const isSmallScreen = useMediaQuery(theme => + theme.breakpoints.down('md'), + ); + const [state, setState] = useState(State.Closed); + const hoverTimerRef = useRef(); const { isPinned } = useContext(SidebarPinStateContext); - const [isOpen, setIsOpen] = useState(isPinned); - useEffect(() => { + const handleOpen = () => { if (isPinned) { - setIsOpen(true); + return; } - }, [isPinned]); + if (hoverTimerRef.current) { + clearTimeout(hoverTimerRef.current); + hoverTimerRef.current = undefined; + } + if (state !== State.Open && !isSmallScreen) { + hoverTimerRef.current = window.setTimeout(() => { + hoverTimerRef.current = undefined; + setState(State.Open); + }, openDelayMs); + + setState(State.Idle); + } + }; + + const handleClose = () => { + if (isPinned) { + return; + } + if (hoverTimerRef.current) { + clearTimeout(hoverTimerRef.current); + hoverTimerRef.current = undefined; + } + if (state === State.Idle) { + setState(State.Closed); + } else if (state === State.Open) { + hoverTimerRef.current = window.setTimeout(() => { + hoverTimerRef.current = undefined; + setState(State.Closed); + }, closeDelayMs); + } + }; + + const isOpen = (state === State.Open && !isSmallScreen) || isPinned; return ( -
+
{} : handleOpen} + onFocus={disableExpandOnHover ? () => {} : handleOpen} + onMouseLeave={disableExpandOnHover ? () => {} : handleClose} + onBlur={disableExpandOnHover ? () => {} : handleClose} + >
) { export const SidebarExpandButton = () => { const classes = useStyles(); + const { isOpen, handleOpen, handleClose } = useContext(SidebarContext); + const { isPinned } = useContext(SidebarPinStateContext); const isSmallScreen = useMediaQuery(theme => theme.breakpoints.down('md'), ); - const { isPinned } = useContext(SidebarPinStateContext); - const { isOpen, setIsOpen } = useContext(SidebarContext); - - const openDelayMs = sidebarConfig.defaultOpenDelayMs; - const closeDelayMs = sidebarConfig.defaultCloseDelayMs; const handleClick = () => { - if (isPinned || isSmallScreen) { - return; + if (isOpen) { + handleClose(); + } else { + handleOpen(); } - const delayMs = isOpen ? openDelayMs : closeDelayMs; - setTimeout(async () => { - setIsOpen(!isOpen); - }, delayMs); }; - if (isSmallScreen || isPinned) { + if (isPinned || isSmallScreen) { return null; } diff --git a/packages/core-components/src/layout/Sidebar/Items.test.tsx b/packages/core-components/src/layout/Sidebar/Items.test.tsx index 34bc454fec..f4d699d09c 100644 --- a/packages/core-components/src/layout/Sidebar/Items.test.tsx +++ b/packages/core-components/src/layout/Sidebar/Items.test.tsx @@ -47,7 +47,7 @@ async function renderSidebar() { , ); - userEvent.click(screen.getByTestId('sidebar-expand-button')); + userEvent.hover(screen.getByTestId('sidebar-root')); } describe('Items', () => { diff --git a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx index 18bfb120e3..c95460553c 100644 --- a/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx +++ b/packages/core-components/src/layout/Sidebar/Sidebar.stories.tsx @@ -53,6 +53,18 @@ const handleSearch = (input: string) => { export const SampleSidebar = () => ( + + + + + + + + +); + +export const SampleScalableSidebar = () => ( + >; + handleOpen: () => any; + handleClose: () => any; }; export const SidebarContext = createContext({ isOpen: false, - setIsOpen: () => {}, + handleOpen: () => {}, + handleClose: () => {}, }); export type ItemWithSubmenuContextType = { diff --git a/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx b/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx index 02415d6fbc..fe37c9c74f 100644 --- a/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx +++ b/packages/core-components/src/layout/Sidebar/icons/CatalogSidebarLogo.tsx @@ -24,6 +24,7 @@ export const CatalogSidebarLogo = () => { height="21" fill="none" viewBox="0 0 20 21" + fontSize="small" > Date: Thu, 4 Nov 2021 14:08:29 +0000 Subject: [PATCH 020/975] Update context passed in Shortcuts.test.tsx and ShortcutItem.test.tsx Signed-off-by: hiba-aldalaty --- plugins/shortcuts/src/ShortcutItem.test.tsx | 4 +++- plugins/shortcuts/src/Shortcuts.test.tsx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/shortcuts/src/ShortcutItem.test.tsx b/plugins/shortcuts/src/ShortcutItem.test.tsx index f452ec095d..c8f5e4b0ca 100644 --- a/plugins/shortcuts/src/ShortcutItem.test.tsx +++ b/plugins/shortcuts/src/ShortcutItem.test.tsx @@ -37,7 +37,9 @@ describe('ShortcutItem', () => { it('displays the shortcut', async () => { await renderInTestApp( - {} }}> + {}, handleClose: () => {} }} + > , ); diff --git a/plugins/shortcuts/src/Shortcuts.test.tsx b/plugins/shortcuts/src/Shortcuts.test.tsx index fa8e6be440..f0c85aaf5f 100644 --- a/plugins/shortcuts/src/Shortcuts.test.tsx +++ b/plugins/shortcuts/src/Shortcuts.test.tsx @@ -30,7 +30,9 @@ const apis = ApiRegistry.from([ describe('Shortcuts', () => { it('displays an add button', async () => { await renderInTestApp( - {} }}> + {}, handleClose: () => {} }} + > From ee69cdbd656cc1a6c838949c227760545e65a1be Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Thu, 4 Nov 2021 14:09:34 +0000 Subject: [PATCH 021/975] Update api-report.md in @backstage/core-components Signed-off-by: hiba-aldalaty --- packages/core-components/api-report.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/packages/core-components/api-report.md b/packages/core-components/api-report.md index ae7a781729..e8bde307b0 100644 --- a/packages/core-components/api-report.md +++ b/packages/core-components/api-report.md @@ -16,7 +16,6 @@ import { ComponentProps } from 'react'; import { Context } from 'react'; import { default as CSS_2 } from 'csstype'; import { CSSProperties } from 'react'; -import { Dispatch } from 'react'; import { ElementType } from 'react'; import { ErrorInfo } from 'react'; import { IconComponent } from '@backstage/core-plugin-api'; @@ -34,7 +33,6 @@ import * as React_3 from 'react'; import { ReactElement } from 'react'; import { ReactNode } from 'react'; import { SessionApi } from '@backstage/core-plugin-api'; -import { SetStateAction } from 'react'; import { SignInPageProps } from '@backstage/core-plugin-api'; import { SparklinesLineProps } from 'react-sparklines'; import { SparklinesProps } from 'react-sparklines'; @@ -102,7 +100,7 @@ export type BottomLinkProps = { // Warning: (ae-forgotten-export) The symbol "Props" needs to be exported by the entry point index.d.ts // // @public (undocumented) -export function Breadcrumbs(props: Props_20): JSX.Element; +export function Breadcrumbs(props: Props_21): JSX.Element; // @public (undocumented) export type BreadcrumbsClickableTextClassKey = 'root'; @@ -769,10 +767,11 @@ 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 function Sidebar({ children }: PropsWithChildren<{}>): JSX.Element; +export function Sidebar(props: 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) // @@ -813,7 +812,8 @@ export const SidebarContext: Context; // @public (undocumented) export type SidebarContextType = { isOpen: boolean; - setIsOpen: Dispatch>; + handleOpen: () => any; + handleClose: () => any; }; // Warning: (ae-missing-release-tag) "SidebarDivider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) @@ -1968,7 +1968,7 @@ export const SidebarSpacer: React_2.ComponentType< // 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_19): 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) // @@ -2151,7 +2151,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) // From f0fcdbfed1ccf68f27c305ab9301f8904a37a446 Mon Sep 17 00:00:00 2001 From: hiba-aldalaty Date: Thu, 4 Nov 2021 14:10:03 +0000 Subject: [PATCH 022/975] Update api-report.md in @backstage/theme Signed-off-by: hiba-aldalaty --- packages/theme/api-report.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/packages/theme/api-report.md b/packages/theme/api-report.md index 6a85bafed6..f4b6d9b79f 100644 --- a/packages/theme/api-report.md +++ b/packages/theme/api-report.md @@ -46,9 +46,6 @@ export type BackstagePaletteAdditions = { }; submenu: { background: string; - indicator: string; - color: string; - selectedColor: string; }; }; tabbar: { From b8b67b574011074720b9f7d252b9ee2005b9a97b Mon Sep 17 00:00:00 2001 From: Gabriele Mambrini Date: Fri, 5 Nov 2021 09:52:50 +0100 Subject: [PATCH 023/975] Add allowed paths to backend.reading.allow Signed-off-by: Gabriele Mambrini --- .../src/reading/FetchUrlReader.test.ts | 7 +++++++ .../backend-common/src/reading/FetchUrlReader.ts | 15 +++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/packages/backend-common/src/reading/FetchUrlReader.test.ts b/packages/backend-common/src/reading/FetchUrlReader.test.ts index 124abb799c..9d6edee765 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.test.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.test.ts @@ -77,6 +77,10 @@ describe('FetchUrlReader', () => { { host: 'example.com:700' }, { host: '*.examples.org' }, { host: '*.examples.org:700' }, + { + host: 'foobar.org', + paths: ['/dir1/'], + }, ], }, }, @@ -106,6 +110,9 @@ describe('FetchUrlReader', () => { expect(predicate(new URL('https://examples.org:700/test'))).toBe(false); expect(predicate(new URL('https://a.examples.org:700/test'))).toBe(true); expect(predicate(new URL('https://a.b.examples.org:700/test'))).toBe(true); + expect(predicate(new URL('https://foobar.org/dir1/subpath'))).toBe(true); + expect(predicate(new URL('https://foobar.org/dir12'))).toBe(false); + expect(predicate(new URL('https://foobar.org/'))).toBe(false); }); describe('read', () => { diff --git a/packages/backend-common/src/reading/FetchUrlReader.ts b/packages/backend-common/src/reading/FetchUrlReader.ts index 732d3b9f59..1ae9677a85 100644 --- a/packages/backend-common/src/reading/FetchUrlReader.ts +++ b/packages/backend-common/src/reading/FetchUrlReader.ts @@ -24,6 +24,7 @@ import { SearchResponse, UrlReader, } from './types'; +import { normalize as normalizePath } from 'path'; /** * A UrlReader that does a plain fetch of the URL. @@ -39,18 +40,28 @@ export class FetchUrlReader implements UrlReader { * `host`: * Either full hostnames to match, or subdomain wildcard matchers with a leading `*`. * For example `example.com` and `*.example.com` are valid values, `prod.*.example.com` is not. + * + * `paths`: + * An optional list of paths which are allowed. If the list is omitted all paths are allowed. */ static factory: ReaderFactory = ({ config }) => { const predicates = config .getOptionalConfigArray('backend.reading.allow') ?.map(allowConfig => { + const paths = allowConfig.getOptionalStringArray('paths'); + const checkPath = paths + ? (url: URL) => { + const targetPath = normalizePath(url.pathname); + return paths.some(path => targetPath.startsWith(path)); + } + : (_url: URL) => true; const host = allowConfig.getString('host'); if (host.startsWith('*.')) { const suffix = host.slice(1); - return (url: URL) => url.host.endsWith(suffix); + return (url: URL) => url.host.endsWith(suffix) && checkPath(url); } - return (url: URL) => url.host === host; + return (url: URL) => url.host === host && checkPath(url); }) ?? []; const reader = new FetchUrlReader(); From bfc837a97b5cd16916ac241067df90e62a149479 Mon Sep 17 00:00:00 2001 From: Gabriele Mambrini Date: Fri, 5 Nov 2021 09:53:45 +0100 Subject: [PATCH 024/975] Update docs for allowed paths in backend.reading.allow Signed-off-by: Gabriele Mambrini --- docs/features/software-catalog/descriptor-format.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 7100e86ed0..57473ce4c7 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -144,7 +144,8 @@ spec: Note that to be able to read from targets that are outside of the normal integration points such as `github.com`, you'll need to explicitly allow it by -adding an entry in the `backend.reading.allow` list. For example: +adding an entry in the `backend.reading.allow` list. Paths can be specified to +further restrict targets For example: ```yml backend: @@ -153,6 +154,8 @@ backend: allow: - host: example.com - host: '*.examples.org' + - host: example.net + paths: ['/api/'] ``` ## Common to All Kinds: The Envelope From 1daada3a06f511ee31614822e7d00cd4f88fd0fe Mon Sep 17 00:00:00 2001 From: Gabriele Mambrini Date: Fri, 5 Nov 2021 10:00:17 +0100 Subject: [PATCH 025/975] Added changeset Signed-off-by: Gabriele Mambrini --- .changeset/nasty-impalas-travel.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/nasty-impalas-travel.md diff --git a/.changeset/nasty-impalas-travel.md b/.changeset/nasty-impalas-travel.md new file mode 100644 index 0000000000..f6eb321628 --- /dev/null +++ b/.changeset/nasty-impalas-travel.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-common': patch +--- + +Paths can be specified in backend.reading.allow to further restrict allowed targets From fb7a0bdeac53d15c02e472e3eaebeb9b16c2aad3 Mon Sep 17 00:00:00 2001 From: Pavel Date: Thu, 28 Oct 2021 10:48:51 +0200 Subject: [PATCH 026/975] Update README.md This is needed, otherwise, after you're redirected back there is an error from browser and nothing works. Signed-off-by: Pavlo Poliakov --- plugins/api-docs/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/plugins/api-docs/README.md b/plugins/api-docs/README.md index d5ed198dc1..be98304cfe 100644 --- a/plugins/api-docs/README.md +++ b/plugins/api-docs/README.md @@ -160,6 +160,18 @@ by this plugin. Grab a copy of [oauth2-redirect.html](https://github.com/swagger-api/swagger-ui/blob/master/dist/oauth2-redirect.html) and put it in the `app/public/` directory in order to enable Swagger UI to complete this redirection. +This also may require you to adjust `Content Security Policy` header settings of your backstage application. So javascript on `oauth2-redirect.html` can be executed. + +There are two steps: +1. Open `oauth2-redirect.html` for editing and add `nonce` to the `script` tag. Like this + ``` +