diff --git a/packages/compass-components/src/components/compass-components-provider.tsx b/packages/compass-components/src/components/compass-components-provider.tsx index 88912779b56..ad648ea378c 100644 --- a/packages/compass-components/src/components/compass-components-provider.tsx +++ b/packages/compass-components/src/components/compass-components-provider.tsx @@ -7,6 +7,7 @@ import { SignalHooksProvider } from './signal-popover'; import { RequiredURLSearchParamsProvider } from './links/link'; import { StackedComponentProvider } from '../hooks/use-stacked-component'; import { ContextMenuProvider } from './context-menu'; +import { DrawerContentProvider } from './drawer-portal'; type GuideCueProviderProps = React.ComponentProps; @@ -131,33 +132,35 @@ export const CompassComponentsProvider = ({ darkMode={darkMode} popoverPortalContainer={popoverPortalContainer} > - - - + + - - - - - {typeof children === 'function' - ? children({ - darkMode, - portalContainerRef: setPortalContainer, - scrollContainerRef: setScrollContainer, - }) - : children} - - - - - - - + + + + + + {typeof children === 'function' + ? children({ + darkMode, + portalContainerRef: setPortalContainer, + scrollContainerRef: setScrollContainer, + }) + : children} + + + + + + + + ); }; diff --git a/packages/compass-components/src/components/drawer-portal.tsx b/packages/compass-components/src/components/drawer-portal.tsx new file mode 100644 index 00000000000..3f6310d4222 --- /dev/null +++ b/packages/compass-components/src/components/drawer-portal.tsx @@ -0,0 +1,133 @@ +import React, { useContext, useEffect, useRef, useState } from 'react'; + +import { + DrawerLayout, + DisplayMode as DrawerDisplayMode, + useDrawerToolbarContext, + type DrawerLayoutProps, +} from './drawer'; + +type ToolbarData = Required['toolbarData']; + +type DrawerStateContextValue = ToolbarData; + +type DrawerActionsContextValue = { + current: { + openDrawer: (id: string) => void; + closeDrawer: () => void; + updateToolbarData: (data: ToolbarData[number]) => void; + }; +}; + +const DrawerStateContext = React.createContext([]); + +const DrawerActionsContext = React.createContext({ + current: { + openDrawer: () => undefined, + closeDrawer: () => undefined, + updateToolbarData: () => undefined, + }, +}); + +/** + * Drawer component that keeps track of drawer rendering state and provides + * context to all places that require it. Separating it from DrawerAnchor and + * DrawerSection allows to freely move the actual drawer around while allowing + * the whole application access to the Drawer state, not only parts of it + * wrapped in the Drawer + * + * @example + * + * function App() { + * return ( + * + * + * + * + * + * ) + * } + * + * function Content() { + * const [showDrawerSection, setShowDrawerSection] = useState(false); + * return ( + * <> + * + * {showDrawerSection && + * + * This will be rendered inside the drawer + * + * )} + * + * ) + * } + */ +export const DrawerContentProvider: React.FunctionComponent = ({ + children, +}) => { + const [drawerState, setDrawerState] = useState([]); + const drawerActions = useRef({ + openDrawer: () => undefined, + closeDrawer: () => undefined, + updateToolbarData: (data: ToolbarData[number]) => { + setDrawerState((prevState) => { + return prevState.map((item) => { + return item.id === data.id ? data : item; + }); + }); + }, + }); + + return ( + + + {children} + + + ); +}; + +const DrawerContextGrabber: React.FunctionComponent = ({ children }) => { + const drawerToolbarContext = useDrawerToolbarContext(); + const actions = useContext(DrawerActionsContext); + actions.current.openDrawer = drawerToolbarContext.openDrawer; + actions.current.closeDrawer = drawerToolbarContext.closeDrawer; + return <>{children}; +}; + +/** + * DrawerAnchor component will render the drawer in any place it is rendered. + * This component has to wrap any content that Drawer will be shown near + */ +export const DrawerAnchor: React.FunctionComponent<{ + displayMode?: DrawerDisplayMode; +}> = ({ displayMode, children }) => { + const toolbarData = useContext(DrawerStateContext); + return ( + + {children} + + ); +}; + +type DrawerSectionProps = ToolbarData[number]; + +/** + * DrawerSection allows to declaratively render sections inside the drawer + * independantly from the Drawer itself + */ +export const DrawerSection: React.FunctionComponent = ({ + children, + ...props +}) => { + const actions = useContext(DrawerActionsContext); + useEffect(() => { + actions.current.updateToolbarData({ ...props, content: children }); + }); + return null; +}; + +export { DrawerDisplayMode }; diff --git a/packages/compass-components/src/components/leafygreen.tsx b/packages/compass-components/src/components/leafygreen.tsx index 01fb30c4892..dd75a3543f9 100644 --- a/packages/compass-components/src/components/leafygreen.tsx +++ b/packages/compass-components/src/components/leafygreen.tsx @@ -134,16 +134,6 @@ const TextInput: typeof LeafyGreenTextInput = React.forwardRef( TextInput.displayName = 'TextInput'; -export { - Drawer, - DrawerLayout, - DisplayMode as DrawerDisplayMode, - DrawerStackProvider, - useDrawerStackContext, - useDrawerToolbarContext, - type DrawerLayoutProps, -} from './drawer'; - // 3. Export the leafygreen components. export { AtlasNavGraphic, diff --git a/packages/compass-components/src/index.ts b/packages/compass-components/src/index.ts index 40d6a609b93..53271cedb83 100644 --- a/packages/compass-components/src/index.ts +++ b/packages/compass-components/src/index.ts @@ -219,3 +219,4 @@ export { export { SelectList } from './components/select-list'; export { ParagraphSkeleton } from '@leafygreen-ui/skeleton-loader'; export { InsightsChip } from './components/insights-chip'; +export * from './components/drawer-portal'; diff --git a/packages/compass-workspaces/src/components/workspaces.tsx b/packages/compass-workspaces/src/components/workspaces.tsx index dc3c0734c98..6e68ca2a10b 100644 --- a/packages/compass-workspaces/src/components/workspaces.tsx +++ b/packages/compass-workspaces/src/components/workspaces.tsx @@ -1,5 +1,6 @@ import React, { useCallback, useMemo } from 'react'; import { + DrawerAnchor, ErrorBoundary, MongoDBLogoMark, WorkspaceTabs, @@ -215,13 +216,16 @@ const CompassWorkspaces: React.FunctionComponent = ({ selectedTabIndex={activeTabIndex} > -
- {activeTab && workspaceTabContent ? ( - workspaceTabContent - ) : ( - - )} -
+ {/* TODO: not only this breaks layout in an unexpected way, it also causes a ton of ResizeObserver errors to pop up, seems like wrapping anything in leafygreen Drawer breaks our virtualized lisis completely */} + +
+ {activeTab && workspaceTabContent ? ( + workspaceTabContent + ) : ( + + )} +
+
); };