diff --git a/workspaces/global-floating-action-button/plugins/global-floating-action-button/src/components/FloatingButton.tsx b/workspaces/global-floating-action-button/plugins/global-floating-action-button/src/components/FloatingButton.tsx index 0a85787cd1..443a481802 100644 --- a/workspaces/global-floating-action-button/plugins/global-floating-action-button/src/components/FloatingButton.tsx +++ b/workspaces/global-floating-action-button/plugins/global-floating-action-button/src/components/FloatingButton.tsx @@ -31,10 +31,10 @@ const useStyles = makeStyles(theme => ({ right: `calc(${theme?.spacing?.(2) ?? '16px'} + 1.5em)`, alignItems: 'end', - // When quickstart drawer is open, adjust margin - '.quickstart-drawer-open &': { + // When drawer is docked, adjust margin + '.docked-drawer-open &': { transition: 'margin-right 0.3s ease', - marginRight: 'var(--quickstart-drawer-width, 500px) ', + marginRight: 'var(--docked-drawer-width, 500px) ', }, }, 'bottom-left': { diff --git a/workspaces/quickstart/packages/app/package.json b/workspaces/quickstart/packages/app/package.json index f3584c70e6..0f1d93bdc4 100644 --- a/workspaces/quickstart/packages/app/package.json +++ b/workspaces/quickstart/packages/app/package.json @@ -49,8 +49,10 @@ "@material-ui/icons": "^4.9.1", "@mui/icons-material": "5.18.0", "@mui/material": "5.18.0", + "@red-hat-developer-hub/backstage-plugin-application-drawer": "workspace:^", "@red-hat-developer-hub/backstage-plugin-global-header": "^1.17.1", "@red-hat-developer-hub/backstage-plugin-quickstart": "workspace:^", + "@red-hat-developer-hub/backstage-plugin-test-drawer": "workspace:^", "@red-hat-developer-hub/backstage-plugin-theme": "^0.11.0", "react": "^18.0.2", "react-dom": "^18.0.2", diff --git a/workspaces/quickstart/packages/app/src/components/Root/ApplicationDrawer.tsx b/workspaces/quickstart/packages/app/src/components/Root/ApplicationDrawer.tsx new file mode 100644 index 0000000000..91ef3aa54f --- /dev/null +++ b/workspaces/quickstart/packages/app/src/components/Root/ApplicationDrawer.tsx @@ -0,0 +1,176 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { + ComponentType, + useState, + useCallback, + useMemo, + useEffect, +} from 'react'; +import { ResizableDrawer } from './ResizableDrawer'; + +/** + * Partial drawer state exposed by drawer plugins + * + * @public + */ +export interface DrawerPartialState { + id: string; + isDrawerOpen: boolean; + drawerWidth: number; + setDrawerWidth: (width: number) => void; +} + +/** + * Props for drawer state exposer components + * + * @public + */ +export interface DrawerStateExposerProps { + onStateChange: (state: DrawerPartialState) => void; + onUnmount?: (id: string) => void; +} + +/** + * Drawer content configuration + */ +type DrawerContentType = { + id: string; + Component: ComponentType; + priority?: number; + resizable?: boolean; +}; + +/** + * State exposer component type + */ +type StateExposerType = { + Component: ComponentType; +}; + +export interface ApplicationDrawerProps { + /** + * Array of drawer content configurations + * Maps drawer IDs to their content components + */ + drawerContents: DrawerContentType[]; + /** + * Array of state exposer components from drawer plugins + * These are typically mounted via `application/drawer-state` mount point + * + * In RHDH dynamic plugins, this would come from: + * ```yaml + * mountPoints: + * - mountPoint: application/drawer-state + * importName: TestDrawerStateExposer + * ``` + */ + stateExposers?: StateExposerType[]; +} + +export const ApplicationDrawer = ({ + drawerContents, + stateExposers = [], +}: ApplicationDrawerProps) => { + // Collect drawer states from all state exposers + const [drawerStates, setDrawerStates] = useState< + Record + >({}); + + // Callback for state exposers to report their state + const handleStateChange = useCallback((state: DrawerPartialState) => { + setDrawerStates(prev => { + // Only update if something actually changed + const existing = prev[state.id]; + if ( + existing && + existing.isDrawerOpen === state.isDrawerOpen && + existing.drawerWidth === state.drawerWidth && + existing.setDrawerWidth === state.setDrawerWidth + ) { + return prev; + } + return { ...prev, [state.id]: state }; + }); + }, []); + + // Convert states record to array + const statesArray = useMemo( + () => Object.values(drawerStates), + [drawerStates], + ); + + // Get active drawer - find the open drawer with highest priority + const activeDrawer = useMemo(() => { + return statesArray + .filter(state => state.isDrawerOpen) + .map(state => { + const content = drawerContents.find(c => c.id === state.id); + if (!content) return null; + return { ...state, ...content }; + }) + .filter(Boolean) + .sort((a, b) => (b?.priority ?? -1) - (a?.priority ?? -1))[0]; + }, [statesArray, drawerContents]); + + // Manage CSS classes and variables for layout adjustments + useEffect(() => { + if (activeDrawer) { + const className = `docked-drawer-open`; + const cssVar = `--docked-drawer-width`; + + document.body.classList.add(className); + document.body.style.setProperty(cssVar, `${activeDrawer.drawerWidth}px`); + + return () => { + document.body.classList.remove(className); + document.body.style.removeProperty(cssVar); + }; + } + return undefined; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [activeDrawer?.id, activeDrawer?.drawerWidth]); + + // Wrapper to handle the width change callback type + const handleWidthChange = useCallback( + (width: number) => { + activeDrawer?.setDrawerWidth(width); + }, + [activeDrawer], + ); + + return ( + <> + {/* Render all state exposers - they return null but report their state */} + {stateExposers.map(({ Component }, index) => ( + + ))} + + {/* Render the active drawer */} + {activeDrawer && ( + + + + )} + + ); +}; diff --git a/workspaces/quickstart/packages/app/src/components/Root/ResizableDrawer.tsx b/workspaces/quickstart/packages/app/src/components/Root/ResizableDrawer.tsx new file mode 100644 index 0000000000..e67e837a44 --- /dev/null +++ b/workspaces/quickstart/packages/app/src/components/Root/ResizableDrawer.tsx @@ -0,0 +1,157 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { useCallback, useEffect, useRef, useState } from 'react'; + +import Box from '@mui/material/Box'; +import Drawer from '@mui/material/Drawer'; +import { styled } from '@mui/material/styles'; + +import { ThemeConfig } from '@red-hat-developer-hub/backstage-plugin-theme'; + +const Handle = styled('div')(({ theme }) => ({ + width: 6, + cursor: 'col-resize', + position: 'absolute', + top: 0, + left: 0, + bottom: 0, + zIndex: 1201, + backgroundColor: theme.palette.divider, +})); + +export type ResizableDrawerProps = { + children: React.ReactNode; + minWidth?: number; + maxWidth?: number; + initialWidth?: number; + isDrawerOpen: boolean; + drawerWidth?: number; + onWidthChange?: (width: number) => void; + isResizable?: boolean; + [key: string]: any; +}; + +export const ResizableDrawer = (props: ResizableDrawerProps) => { + const { + children, + minWidth = 400, + maxWidth = 800, + initialWidth = 400, + isDrawerOpen, + isResizable = false, + drawerWidth: externalDrawerWidth, + onWidthChange, + ...drawerProps + } = props; + + // Ensure width is never below minWidth + const clampedInitialWidth = Math.max( + externalDrawerWidth || initialWidth, + minWidth, + ); + + const [width, setWidth] = useState(clampedInitialWidth); + const resizingRef = useRef(false); + + // Sync with external drawerWidth when it changes + useEffect(() => { + if (externalDrawerWidth !== undefined) { + const clampedWidth = Math.max(externalDrawerWidth, minWidth); + if (clampedWidth !== width) { + setWidth(clampedWidth); + // If the external width was below min, update the parent + if (externalDrawerWidth < minWidth && onWidthChange && isResizable) { + onWidthChange(clampedWidth); + } + } + } + }, [externalDrawerWidth, width, minWidth, onWidthChange, isResizable]); + + const onMouseDown = () => { + resizingRef.current = true; + }; + + const onMouseMove = useCallback( + (e: MouseEvent) => { + if (!resizingRef.current) return; + // For right-anchored drawer, calculate width from the right edge + const newWidth = window.innerWidth - e.clientX; + + if (newWidth >= minWidth && newWidth <= maxWidth) { + setWidth(newWidth); + if (onWidthChange) { + onWidthChange(newWidth); + } + } + }, + [maxWidth, minWidth, onWidthChange], + ); + + const onMouseUp = () => { + resizingRef.current = false; + }; + + useEffect(() => { + if (isResizable) { + window.addEventListener('mousemove', onMouseMove); + window.addEventListener('mouseup', onMouseUp); + return () => { + window.removeEventListener('mousemove', onMouseMove); + window.removeEventListener('mouseup', onMouseUp); + }; + } + return () => {}; + }, [onMouseMove, isResizable]); + + // Ensure anchor is always 'right' and not overridden by drawerProps + const { anchor: _, ...restDrawerProps } = drawerProps; + + return ( + { + const themeConfig = theme as ThemeConfig; + return ( + themeConfig.palette?.rhdh?.general?.sidebarBackgroundColor || + theme.palette.background.paper + ); + }, + justifyContent: 'space-between', + }, + // Only apply header offset when global header exists + 'body:has(#global-header) &': { + '& .v5-MuiDrawer-paper': { + top: '64px !important', + height: 'calc(100vh - 64px) !important', + }, + }, + }} + variant="persistent" + open={isDrawerOpen} + > + + {children} + {isResizable && } + + + ); +}; \ No newline at end of file diff --git a/workspaces/quickstart/packages/app/src/components/Root/Root.tsx b/workspaces/quickstart/packages/app/src/components/Root/Root.tsx index 9731ec6cc9..6161d0d420 100644 --- a/workspaces/quickstart/packages/app/src/components/Root/Root.tsx +++ b/workspaces/quickstart/packages/app/src/components/Root/Root.tsx @@ -47,9 +47,20 @@ import { GlobalHeaderComponent, } from '@red-hat-developer-hub/backstage-plugin-global-header'; import Box from '@mui/material/Box'; -import { QuickstartDrawerProvider } from '@red-hat-developer-hub/backstage-plugin-quickstart'; +import { + QuickstartDrawerProvider, + QuickstartDrawerContent, + QuickstartDrawerStateExposer, +} from '@red-hat-developer-hub/backstage-plugin-quickstart'; import { QuickstartSidebarItem } from './QuickstartSidebarItem'; import { Administration } from '@backstage-community/plugin-rbac'; +import { + TestDrawerContent, + TestDrawerProvider, + TestDrawerStateExposer, +} from '@red-hat-developer-hub/backstage-plugin-test-drawer'; +import { ApplicationDrawer } from './ApplicationDrawer'; +import { TestDrawerSidebarItem } from './TestDrawerSidebarItem'; const useSidebarLogoStyles = makeStyles({ root: { @@ -88,9 +99,9 @@ export const Root = ({ children }: PropsWithChildren<{}>) => { // This code exists similarly in RHDH: // https://github.com/redhat-developer/rhdh/blob/main/packages/app/src/components/Root/Root.tsx#L159-L165 // https://github.com/redhat-developer/rhdh/blob/main/packages/app/src/components/ErrorPages/ErrorPage.tsx#L54-L59 - 'body.quickstart-drawer-open #sidebar&': { + 'body.docked-drawer-open #sidebar&': { "> div > main[class*='BackstagePage-root']": { - marginRight: 'calc(var(--quickstart-drawer-width, 500px) + 1.5em)', + marginRight: 'calc(var(--docked-drawer-width, 500px) + 1.5em)', transition: 'margin-right 0.3s ease', }, }, @@ -101,46 +112,71 @@ export const Root = ({ children }: PropsWithChildren<{}>) => { globalHeaderMountPoints={defaultGlobalHeaderComponentsMountPoints} /> - - - } to="/search"> - - - - }> - {/* Global nav, not org-specific */} - - - - - - {/* End global nav */} + + + + } to="/search"> + + + + }> + {/* Global nav, not org-specific */} + + + + + + {/* End global nav */} + + + + {/* Items in this group will be scrollable if they run out of space */} + + + + + - - - {/* Items in this group will be scrollable if they run out of space */} - - - - - - } - to="/settings" - > - - - - {children} + } + to="/settings" + > + + + + {children} + + diff --git a/workspaces/quickstart/packages/app/src/components/Root/TestDrawerSidebarItem.tsx b/workspaces/quickstart/packages/app/src/components/Root/TestDrawerSidebarItem.tsx new file mode 100644 index 0000000000..0b47afcd90 --- /dev/null +++ b/workspaces/quickstart/packages/app/src/components/Root/TestDrawerSidebarItem.tsx @@ -0,0 +1,26 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { SidebarItem, StarIcon } from '@backstage/core-components'; +import { useTestDrawerContext } from '@red-hat-developer-hub/backstage-plugin-test-drawer'; + +export const TestDrawerSidebarItem = () => { + const { toggleDrawer } = useTestDrawerContext(); + + return ( + + ); +}; diff --git a/workspaces/quickstart/plugins/quickstart/dev/index.tsx b/workspaces/quickstart/plugins/quickstart/dev/index.tsx index 1b36d1335f..6e4f575190 100644 --- a/workspaces/quickstart/plugins/quickstart/dev/index.tsx +++ b/workspaces/quickstart/plugins/quickstart/dev/index.tsx @@ -25,6 +25,8 @@ import Card from '@mui/material/Card'; import CardContent from '@mui/material/CardContent'; import Button from '@mui/material/Button'; import { getAllThemes } from '@red-hat-developer-hub/backstage-plugin-theme'; +import { QuickstartDrawerContent } from '../src/components/QuickstartDrawerContent'; +import { DrawerComponent } from '../src/components/DrawerComponent'; const QuickstartTestPageContent = () => { const { openDrawer, closeDrawer, isDrawerOpen } = @@ -99,6 +101,9 @@ const QuickstartTestPageContent = () => { const QuickstartTestPage = () => ( + + + ); diff --git a/workspaces/quickstart/plugins/quickstart/package.json b/workspaces/quickstart/plugins/quickstart/package.json index aef5ae8a05..fbc3c13b55 100644 --- a/workspaces/quickstart/plugins/quickstart/package.json +++ b/workspaces/quickstart/plugins/quickstart/package.json @@ -37,6 +37,7 @@ "@backstage/theme": "^0.7.0", "@mui/icons-material": "5.18.0", "@mui/material": "5.18.0", + "@red-hat-developer-hub/backstage-plugin-application-drawer": "^0.1.0", "@red-hat-developer-hub/backstage-plugin-theme": "^0.11.0", "react-use": "^17.6.0" }, diff --git a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawer.tsx b/workspaces/quickstart/plugins/quickstart/src/components/DrawerComponent.tsx similarity index 83% rename from workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawer.tsx rename to workspaces/quickstart/plugins/quickstart/src/components/DrawerComponent.tsx index 76856b34ee..c14c9bcf47 100644 --- a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawer.tsx +++ b/workspaces/quickstart/plugins/quickstart/src/components/DrawerComponent.tsx @@ -14,18 +14,17 @@ * limitations under the License. */ +import { PropsWithChildren, useMemo } from 'react'; import Drawer from '@mui/material/Drawer'; import { ThemeConfig } from '@red-hat-developer-hub/backstage-plugin-theme'; import { configApiRef, useApiHolder } from '@backstage/core-plugin-api'; -import { Quickstart } from './Quickstart'; import { useQuickstartDrawerContext } from '../hooks/useQuickstartDrawerContext'; import { QuickstartItemData } from '../types'; import { filterQuickstartItemsByRole } from '../utils'; // Role is now provided through context to avoid re-fetching on drawer open/close -import { useMemo } from 'react'; -export const QuickstartDrawer = () => { - const { isDrawerOpen, closeDrawer, drawerWidth, userRole, roleLoading } = +export const DrawerComponent = ({ children }: PropsWithChildren) => { + const { isDrawerOpen, drawerWidth, userRole, roleLoading } = useQuickstartDrawerContext(); const apiHolder = useApiHolder(); @@ -43,11 +42,6 @@ export const QuickstartDrawer = () => { : []; }, [roleLoading, userRole, quickstartItems]); - // Only expose items to the body when drawer is open to avoid re-renders during close - const filteredItems = useMemo(() => { - return isDrawerOpen ? eligibleItems : []; - }, [isDrawerOpen, eligibleItems]); - // No auto-open logic here; the provider initializes per user (visited/open) // If no quickstart items are configured at all, don't render the drawer to avoid reserving space @@ -87,11 +81,7 @@ export const QuickstartDrawer = () => { anchor="right" open={isDrawerOpen} > - + {children} ); }; diff --git a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContent.tsx b/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContent.tsx new file mode 100644 index 0000000000..c5268acec0 --- /dev/null +++ b/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContent.tsx @@ -0,0 +1,69 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { useMemo } from 'react'; +import { configApiRef, useApiHolder } from '@backstage/core-plugin-api'; +import { Quickstart } from './Quickstart'; +import { useQuickstartDrawerContext } from '../hooks/useQuickstartDrawerContext'; +import { QuickstartItemData } from '../types'; +import { filterQuickstartItemsByRole } from '../utils'; + +export const QuickstartDrawerContent = () => { + const { isDrawerOpen, closeDrawer, userRole, roleLoading } = + useQuickstartDrawerContext(); + + const apiHolder = useApiHolder(); + const config = apiHolder.get(configApiRef); + const quickstartItems: QuickstartItemData[] = useMemo(() => { + return config?.has('app.quickstart') + ? (config.get('app.quickstart') as QuickstartItemData[]) + : []; + }, [config]); + + // Items available to the user based on role from context + const eligibleItems = useMemo(() => { + return !roleLoading && userRole + ? filterQuickstartItemsByRole(quickstartItems, userRole) + : []; + }, [roleLoading, userRole, quickstartItems]); + + // Only expose items to the body when drawer is open to avoid re-renders during close + const filteredItems = useMemo(() => { + return isDrawerOpen ? eligibleItems : []; + }, [isDrawerOpen, eligibleItems]); + + // No auto-open logic here; the provider initializes per user (visited/open) + + // If no quickstart items are configured at all, don't render the drawer to avoid reserving space + if (quickstartItems.length === 0) { + return null; + } + + // If there are no items for the user, hide the drawer entirely + if (!roleLoading && eligibleItems.length === 0) { + return null; + } + + // No role-fetching or filtering here when the drawer is closed + + return ( + + ); +}; diff --git a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContext.tsx b/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContext.tsx index c89741296b..2f9a360c4f 100644 --- a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContext.tsx +++ b/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerContext.tsx @@ -23,6 +23,7 @@ import { UserRole } from '../types'; * @public */ export interface QuickstartDrawerContextType { + id: string; /** * The prop to check if the drawer is open */ diff --git a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerProvider.tsx b/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerProvider.tsx index 1db449e068..825a023bf0 100644 --- a/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerProvider.tsx +++ b/workspaces/quickstart/plugins/quickstart/src/components/QuickstartDrawerProvider.tsx @@ -24,11 +24,12 @@ import Snackbar from '@mui/material/Snackbar'; import CloseIcon from '@mui/icons-material/Close'; import IconButton from '@mui/material/IconButton'; import { QuickstartDrawerContext } from './QuickstartDrawerContext'; -import { QuickstartDrawer } from './QuickstartDrawer'; import { QuickstartItemData } from '../types'; import { filterQuickstartItemsByRole } from '../utils'; import { useQuickstartRole } from '../hooks/useQuickstartRole'; +const DRAWER_ID = 'quickstart'; + /** * Provider component for the Quickstart Drawer functionality * @public @@ -45,25 +46,6 @@ export const QuickstartDrawerProvider = ({ children }: PropsWithChildren) => { // Determine role once at provider level to avoid re-fetching on drawer open/close const { isLoading: roleLoading, userRole } = useQuickstartRole(); - // Single useEffect - sets class on document.body - useEffect(() => { - if (isDrawerOpen) { - document.body.classList.add('quickstart-drawer-open'); - document.body.style.setProperty( - '--quickstart-drawer-width', - `${drawerWidth}px`, - ); - } else { - document.body.classList.remove('quickstart-drawer-open'); - document.body.style.removeProperty('--quickstart-drawer-width'); - } - - return () => { - document.body.classList.remove('quickstart-drawer-open'); - document.body.style.removeProperty('--quickstart-drawer-width'); - }; - }, [isDrawerOpen, drawerWidth]); - // Resolve the current user's identity to scope localStorage keys per user useEffect(() => { let cancelled = false; @@ -190,6 +172,7 @@ export const QuickstartDrawerProvider = ({ children }: PropsWithChildren) => { return ( { }} > {children} - void; +}; + +/** + * Props for drawer state exposer components + * + * @public + */ +export type DrawerStateExposerProps = { + /** + * Callback called whenever the drawer state changes + */ + onStateChange: (state: DrawerPartialState) => void; +}; + +/** + * This exposes Quickstart Drawer's partial context to the ApplicationDrawer + * + * It reads the QuickstartDrawerContext and calls the onStateChange callback with the + * partial state (id, isDrawerOpen, drawerWidth, setDrawerWidth). + * + * @public + */ +export const QuickstartDrawerStateExposer = ({ + onStateChange, +}: DrawerStateExposerProps) => { + const { id, isDrawerOpen, drawerWidth, setDrawerWidth } = + useQuickstartDrawerContext(); + + useEffect(() => { + onStateChange({ + id, + isDrawerOpen, + drawerWidth, + setDrawerWidth, + }); + }, [id, isDrawerOpen, drawerWidth, onStateChange, setDrawerWidth]); + + return null; +}; diff --git a/workspaces/quickstart/plugins/quickstart/src/index.ts b/workspaces/quickstart/plugins/quickstart/src/index.ts index a9e3cfb234..55413c0272 100644 --- a/workspaces/quickstart/plugins/quickstart/src/index.ts +++ b/workspaces/quickstart/plugins/quickstart/src/index.ts @@ -26,6 +26,7 @@ export * from './plugin'; export { useQuickstartDrawerContext } from './hooks/useQuickstartDrawerContext'; export type { QuickstartDrawerContextType } from './components/QuickstartDrawerContext'; +export { QuickstartDrawerStateExposer } from './components/QuickstartDrawerStateExposer'; /** * @public */ diff --git a/workspaces/quickstart/plugins/quickstart/src/plugin.ts b/workspaces/quickstart/plugins/quickstart/src/plugin.ts index 5fda53cc00..2459bf7b2d 100644 --- a/workspaces/quickstart/plugins/quickstart/src/plugin.ts +++ b/workspaces/quickstart/plugins/quickstart/src/plugin.ts @@ -55,6 +55,23 @@ export const QuickstartDrawerProvider: React.ComponentType = }), ); +/** + * Quickstart Drawer Content component extension + * + * @public + */ +export const QuickstartDrawerContent = quickstartPlugin.provide( + createComponentExtension({ + name: 'QuickstartDrawerContent', + component: { + lazy: () => + import('./components/QuickstartDrawerContent').then( + m => m.QuickstartDrawerContent, + ), + }, + }), +); + /** * Quick start button for global header help dropdown * @@ -72,3 +89,20 @@ export const QuickstartButton: React.ComponentType = }, }), ); + +/** + * Quickstart Drawer State Exposer exposes its drawer state + * + * @public + */ +export const QuickstartDrawerStateExposer = quickstartPlugin.provide( + createComponentExtension({ + name: 'QuickstartDrawerStateExposer', + component: { + lazy: () => + import('./components/QuickstartDrawerStateExposer').then( + m => m.QuickstartDrawerStateExposer, + ), + }, + }), +); diff --git a/workspaces/quickstart/plugins/test-drawer/README.md b/workspaces/quickstart/plugins/test-drawer/README.md new file mode 100644 index 0000000000..9d5d314af9 --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/README.md @@ -0,0 +1,79 @@ +# Test Drawer Plugin + +A test drawer plugin for Backstage that demonstrates how to create drawer components with context-based state management. + +## Getting Started + +This plugin can be accessed by running `yarn start` from this directory, and then navigating to [/test-drawer](http://localhost:3000/test-drawer). + +## Components + +### TestDrawerProvider + +Provider component that wraps your application and provides drawer context and the MUI Drawer component. + +```tsx +import { TestDrawerProvider } from '@red-hat-developer-hub/backstage-plugin-test-drawer'; + +export const App = () => ( + + {/* Your app content */} + +); +``` + +### TestDrawerContent + +The content component that renders inside the MUI Drawer. It includes a header with close button, main content area, and footer. + +### TestDrawerButton + +A button component that can be placed anywhere to toggle the drawer. + +```tsx +import { TestDrawerButton } from '@red-hat-developer-hub/backstage-plugin-test-drawer'; + +// Use in header or toolbar + +``` + +### useTestDrawerContext + +Hook to access the drawer context from any component within the provider. + +```tsx +import { useTestDrawerContext } from '@red-hat-developer-hub/backstage-plugin-test-drawer'; + +const MyComponent = () => { + const { isDrawerOpen, openDrawer, closeDrawer, toggleDrawer, drawerWidth, setDrawerWidth } = useTestDrawerContext(); + + return ( + + ); +}; +``` + +## Context API + +The `TestDrawerContextType` provides: + +| Property | Type | Description | +|----------|------|-------------| +| `isDrawerOpen` | `boolean` | Whether the drawer is currently open | +| `openDrawer` | `() => void` | Function to open the drawer | +| `closeDrawer` | `() => void` | Function to close the drawer | +| `toggleDrawer` | `() => void` | Function to toggle the drawer state | +| `drawerWidth` | `number` | Current drawer width in pixels | +| `setDrawerWidth` | `Dispatch>` | Function to set the drawer width | + +## CSS Variables + +When the drawer is open, the following CSS class and variable are set on `document.body`: + +- Class: `test-drawer-open` +- Variable: `--test-drawer-width` (e.g., `400px`) + +This allows you to adjust other UI elements when the drawer is open. + diff --git a/workspaces/quickstart/plugins/test-drawer/dev/index.tsx b/workspaces/quickstart/plugins/test-drawer/dev/index.tsx new file mode 100644 index 0000000000..2bcf8010de --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/dev/index.tsx @@ -0,0 +1,84 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { createDevApp } from '@backstage/dev-utils'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; +import Typography from '@mui/material/Typography'; +import { + TestDrawerContent, + testDrawerPlugin, + TestDrawerProvider, + useTestDrawerContext, +} from '../src'; +import { DrawerComponent } from '../src/components'; + +const TestPage = () => { + const { toggleDrawer, isDrawerOpen, drawerWidth } = useTestDrawerContext(); + + return ( + + + Test Drawer Plugin + + + + This page demonstrates the Test Drawer plugin functionality. + + + + + + + + Drawer State: + + Is Open: {isDrawerOpen ? 'Yes' : 'No'} + + + Width: {drawerWidth}px + + + + ); +}; + +const DevPage = () => ( + + + + + + +); + +createDevApp() + .registerPlugin(testDrawerPlugin) + .addPage({ + element: , + title: 'Test Drawer', + path: '/test-drawer', + }) + .render(); diff --git a/workspaces/quickstart/plugins/test-drawer/package.json b/workspaces/quickstart/plugins/test-drawer/package.json new file mode 100644 index 0000000000..caefcf7edc --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/package.json @@ -0,0 +1,59 @@ +{ + "name": "@red-hat-developer-hub/backstage-plugin-test-drawer", + "version": "0.1.0", + "license": "Apache-2.0", + "main": "src/index.ts", + "types": "src/index.ts", + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "https://github.com/redhat-developer/rhdh-plugins", + "directory": "workspaces/quickstart/plugins/test-drawer" + }, + "backstage": { + "role": "frontend-plugin", + "pluginId": "test-drawer", + "pluginPackages": [ + "@red-hat-developer-hub/backstage-plugin-test-drawer" + ] + }, + "sideEffects": false, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/core-components": "^0.18.3", + "@backstage/core-plugin-api": "^1.12.0", + "@backstage/theme": "^0.7.0", + "@mui/icons-material": "5.18.0", + "@mui/material": "5.18.0", + "@red-hat-developer-hub/backstage-plugin-application-drawer": "^0.1.0", + "@red-hat-developer-hub/backstage-plugin-theme": "^0.11.0" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "devDependencies": { + "@backstage/cli": "^0.34.5", + "@backstage/dev-utils": "^1.1.17", + "@backstage/test-utils": "^1.7.13", + "@testing-library/jest-dom": "^6.0.0", + "@testing-library/react": "^14.0.0", + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + }, + "files": [ + "dist" + ] +} diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/DrawerComponent.tsx b/workspaces/quickstart/plugins/test-drawer/src/components/DrawerComponent.tsx new file mode 100644 index 0000000000..43620f28a0 --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/DrawerComponent.tsx @@ -0,0 +1,34 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { PropsWithChildren } from 'react'; +import { useTestDrawerContext } from './TestDrawerContext'; +import { ResizableDrawer } from '../../../../packages/app/src/components/Root/ResizableDrawer'; + +export const DrawerComponent = ({ children }: PropsWithChildren) => { + const { isDrawerOpen, drawerWidth, setDrawerWidth } = useTestDrawerContext(); + + return ( + + {children} + + ); +}; diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerButton.tsx b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerButton.tsx new file mode 100644 index 0000000000..2fd840402b --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerButton.tsx @@ -0,0 +1,78 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 MenuItem from '@mui/material/MenuItem'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import { useTheme } from '@mui/material/styles'; +import { useTestDrawerContext } from './TestDrawerContext'; +import { useCallback } from 'react'; + +/** + * Button component to toggle the Test Drawer + * + * Can be used in the global header help dropdown + * + * @public + */ +export const TestDrawerButton = ({ + onClick = () => {}, +}: { + onClick: () => void; +}) => { + const { toggleDrawer } = useTestDrawerContext(); + const theme = useTheme(); + + const handleClick = useCallback(() => { + toggleDrawer(); + onClick(); + }, [toggleDrawer, onClick]); + + return ( + + + + + + Test Drawer + + + + + + ); +}; diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerContent.tsx b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerContent.tsx new file mode 100644 index 0000000000..f0a8fabacb --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerContent.tsx @@ -0,0 +1,121 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import IconButton from '@mui/material/IconButton'; +import List from '@mui/material/List'; +import ListItem from '@mui/material/ListItem'; +import ListItemText from '@mui/material/ListItemText'; +import ListItemIcon from '@mui/material/ListItemIcon'; +import CloseIcon from '@mui/icons-material/Close'; +import CheckCircleIcon from '@mui/icons-material/CheckCircle'; +import { useTestDrawerContext } from './TestDrawerContext'; + +/** + * Content to be rendered inside the Test Drawer + * + * @public + */ +export const TestDrawerContent = () => { + const { toggleDrawer } = useTestDrawerContext(); + + return ( + + + + Test Drawer + + + + + + + {/* Content */} + + + This is a test drawer component that demonstrates how drawer content + can be structured and rendered inside an MUI Drawer. + + + + Features: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Test Drawer Plugin v0.1.0 + + + + ); +}; + diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerContext.tsx b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerContext.tsx new file mode 100644 index 0000000000..46e8406500 --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerContext.tsx @@ -0,0 +1,67 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { createContext, useContext } from 'react'; + +/** + * Type for TestDrawerContext + * + * @public + */ +export interface TestDrawerContextType { + id: string; + /** + * Whether the drawer is open + */ + isDrawerOpen: boolean; + /** + * Function to toggle the drawer state + */ + toggleDrawer: () => void; + /** + * Current drawer width in pixels + */ + drawerWidth: number; + /** + * Function to set the drawer width + */ + setDrawerWidth: React.Dispatch>; +} + +/** + * Context for the Test Drawer + * + * @public + */ +export const TestDrawerContext = createContext< + TestDrawerContextType | undefined +>(undefined); + +/** + * Hook to access the TestDrawerContext + * + * @public + */ +export const useTestDrawerContext = (): TestDrawerContextType => { + const context = useContext(TestDrawerContext); + if (!context) { + throw new Error( + 'useTestDrawerContext must be used within a TestDrawerProvider', + ); + } + return context; +}; + diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerProvider.tsx b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerProvider.tsx new file mode 100644 index 0000000000..2b8352c66a --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerProvider.tsx @@ -0,0 +1,60 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { PropsWithChildren, useState, useCallback } from 'react'; +import { TestDrawerContext } from './TestDrawerContext'; + +const DRAWER_ID = 'test-drawer'; +const DEFAULT_DRAWER_WIDTH = 400; +const MIN_DRAWER_WIDTH = 300; +const MAX_DRAWER_WIDTH = 800; + +/** + * Provider component for the Test Drawer functionality + * + * @public + */ +export const TestDrawerProvider = ({ children }: PropsWithChildren) => { + const [isDrawerOpen, setIsDrawerOpen] = useState(false); + const [drawerWidth, setDrawerWidth] = useState(DEFAULT_DRAWER_WIDTH); + + const toggleDrawer = useCallback(() => { + setIsDrawerOpen(prev => !prev); + }, []); + + // Constrain drawer width to min/max bounds + const handleSetDrawerWidth: React.Dispatch> = + useCallback(value => { + setDrawerWidth(prev => { + const newWidth = typeof value === 'function' ? value(prev) : value; + return Math.min(MAX_DRAWER_WIDTH, Math.max(MIN_DRAWER_WIDTH, newWidth)); + }); + }, []); + + return ( + + {children} + + ); +}; diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerStateExposer.tsx b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerStateExposer.tsx new file mode 100644 index 0000000000..f25cdca0e9 --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/TestDrawerStateExposer.tsx @@ -0,0 +1,68 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { useEffect } from 'react'; +import { useTestDrawerContext } from './TestDrawerContext'; + +/** + * Partial Test drawer state exposed to the ApplicationDrawer + * + * @public + */ +export type DrawerState = { + id: string; + isDrawerOpen: boolean; + drawerWidth: number; + setDrawerWidth: (width: number) => void; +}; + +/** + * Props for drawer state exposer components + * + * @public + */ +export type DrawerStateExposerProps = { + /** + * Callback called whenever the drawer state changes + */ + onStateChange: (state: DrawerState) => void; +}; + +/** + * This exposes TestDrawer's partial context to the ApplicationDrawer + * + * It reads the TestDrawerContext and calls the onStateChange callback with the + * partial state (id, isDrawerOpen, drawerWidth, setDrawerWidth). + * + * @public + */ +export const TestDrawerStateExposer = ({ + onStateChange, +}: DrawerStateExposerProps) => { + const { id, isDrawerOpen, drawerWidth, setDrawerWidth } = + useTestDrawerContext(); + + useEffect(() => { + onStateChange({ + id, + isDrawerOpen, + drawerWidth, + setDrawerWidth, + }); + }, [id, isDrawerOpen, drawerWidth, onStateChange, setDrawerWidth]); + + return null; +}; diff --git a/workspaces/quickstart/plugins/test-drawer/src/components/index.ts b/workspaces/quickstart/plugins/test-drawer/src/components/index.ts new file mode 100644 index 0000000000..bfda8c19fd --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/components/index.ts @@ -0,0 +1,23 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { TestDrawerContext, useTestDrawerContext } from './TestDrawerContext'; +export type { TestDrawerContextType } from './TestDrawerContext'; +export { TestDrawerContent } from './TestDrawerContent'; +export { TestDrawerProvider } from './TestDrawerProvider'; +export { TestDrawerButton } from './TestDrawerButton'; +export { DrawerComponent } from './DrawerComponent'; +export { TestDrawerStateExposer } from './TestDrawerStateExposer'; diff --git a/workspaces/quickstart/plugins/test-drawer/src/index.ts b/workspaces/quickstart/plugins/test-drawer/src/index.ts new file mode 100644 index 0000000000..eec03a32e4 --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/index.ts @@ -0,0 +1,29 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { + testDrawerPlugin, + TestDrawerProvider, + TestDrawerContent, + TestDrawerButton, +} from './plugin'; + +export { + TestDrawerContext, + useTestDrawerContext, + TestDrawerStateExposer, +} from './components'; +export type { TestDrawerContextType } from './components'; diff --git a/workspaces/quickstart/plugins/test-drawer/src/plugin.ts b/workspaces/quickstart/plugins/test-drawer/src/plugin.ts new file mode 100644 index 0000000000..6dd233a959 --- /dev/null +++ b/workspaces/quickstart/plugins/test-drawer/src/plugin.ts @@ -0,0 +1,93 @@ +/* + * Copyright Red Hat, Inc. + * + * 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 { + createPlugin, + createComponentExtension, +} from '@backstage/core-plugin-api'; + +/** + * Test Drawer Plugin + * + * @public + */ +export const testDrawerPlugin = createPlugin({ + id: 'test-drawer', +}); + +/** + * Test Drawer Provider component extension + * + * @public + */ +export const TestDrawerProvider = testDrawerPlugin.provide( + createComponentExtension({ + name: 'TestDrawerProvider', + component: { + lazy: () => + import('./components/TestDrawerProvider').then( + m => m.TestDrawerProvider, + ), + }, + }), +); + +/** + * Test Drawer Content component extension + * + * @public + */ +export const TestDrawerContent = testDrawerPlugin.provide( + createComponentExtension({ + name: 'TestDrawerContent', + component: { + lazy: () => + import('./components/TestDrawerContent').then(m => m.TestDrawerContent), + }, + }), +); + +/** + * Test Drawer Button component extension + * + * @public + */ +export const TestDrawerButton = testDrawerPlugin.provide( + createComponentExtension({ + name: 'TestDrawerButton', + component: { + lazy: () => + import('./components/TestDrawerButton').then(m => m.TestDrawerButton), + }, + }), +); + +/** + * Test Drawer State Exposer exposes its drawer state + * + * @public + */ +export const TestDrawerStateExposer = testDrawerPlugin.provide( + createComponentExtension({ + name: 'TestDrawerStateExposer', + component: { + lazy: () => + import('./components/TestDrawerStateExposer').then( + m => m.TestDrawerStateExposer, + ), + }, + }), +); diff --git a/workspaces/quickstart/yarn.lock b/workspaces/quickstart/yarn.lock index 7cc04947e2..bc220e164f 100644 --- a/workspaces/quickstart/yarn.lock +++ b/workspaces/quickstart/yarn.lock @@ -10996,6 +10996,27 @@ __metadata: languageName: node linkType: hard +"@red-hat-developer-hub/backstage-plugin-application-drawer@^0.1.0, @red-hat-developer-hub/backstage-plugin-application-drawer@workspace:^, @red-hat-developer-hub/backstage-plugin-application-drawer@workspace:plugins/application-drawer": + version: 0.0.0-use.local + resolution: "@red-hat-developer-hub/backstage-plugin-application-drawer@workspace:plugins/application-drawer" + dependencies: + "@backstage/cli": ^0.34.5 + "@backstage/core-components": ^0.18.3 + "@backstage/core-plugin-api": ^1.12.0 + "@backstage/dev-utils": ^1.1.17 + "@backstage/test-utils": ^1.7.13 + "@backstage/theme": ^0.7.0 + "@mui/icons-material": 5.18.0 + "@mui/material": 5.18.0 + "@red-hat-developer-hub/backstage-plugin-theme": ^0.11.0 + "@testing-library/jest-dom": ^6.0.0 + "@testing-library/react": ^14.0.0 + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + languageName: unknown + linkType: soft + "@red-hat-developer-hub/backstage-plugin-global-header@npm:^1.17.1": version: 1.17.1 resolution: "@red-hat-developer-hub/backstage-plugin-global-header@npm:1.17.1" @@ -11044,6 +11065,7 @@ __metadata: "@backstage/theme": ^0.7.0 "@mui/icons-material": 5.18.0 "@mui/material": 5.18.0 + "@red-hat-developer-hub/backstage-plugin-application-drawer": ^0.1.0 "@red-hat-developer-hub/backstage-plugin-theme": ^0.11.0 "@testing-library/jest-dom": ^6.0.0 "@testing-library/react": ^14.0.0 @@ -11056,6 +11078,28 @@ __metadata: languageName: unknown linkType: soft +"@red-hat-developer-hub/backstage-plugin-test-drawer@workspace:^, @red-hat-developer-hub/backstage-plugin-test-drawer@workspace:plugins/test-drawer": + version: 0.0.0-use.local + resolution: "@red-hat-developer-hub/backstage-plugin-test-drawer@workspace:plugins/test-drawer" + dependencies: + "@backstage/cli": ^0.34.5 + "@backstage/core-components": ^0.18.3 + "@backstage/core-plugin-api": ^1.12.0 + "@backstage/dev-utils": ^1.1.17 + "@backstage/test-utils": ^1.7.13 + "@backstage/theme": ^0.7.0 + "@mui/icons-material": 5.18.0 + "@mui/material": 5.18.0 + "@red-hat-developer-hub/backstage-plugin-application-drawer": ^0.1.0 + "@red-hat-developer-hub/backstage-plugin-theme": ^0.11.0 + "@testing-library/jest-dom": ^6.0.0 + "@testing-library/react": ^14.0.0 + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + languageName: unknown + linkType: soft + "@red-hat-developer-hub/backstage-plugin-theme@npm:^0.11.0": version: 0.11.0 resolution: "@red-hat-developer-hub/backstage-plugin-theme@npm:0.11.0" @@ -15318,8 +15362,10 @@ __metadata: "@mui/icons-material": 5.18.0 "@mui/material": 5.18.0 "@playwright/test": 1.57.0 + "@red-hat-developer-hub/backstage-plugin-application-drawer": "workspace:^" "@red-hat-developer-hub/backstage-plugin-global-header": ^1.17.1 "@red-hat-developer-hub/backstage-plugin-quickstart": "workspace:^" + "@red-hat-developer-hub/backstage-plugin-test-drawer": "workspace:^" "@red-hat-developer-hub/backstage-plugin-theme": ^0.11.0 "@testing-library/dom": ^9.0.0 "@testing-library/jest-dom": ^6.0.0