Navigation drawer refactor (#11251)

closes #11195
closes #11199

### Context

The yellow dots in the Settings Navigation Drawer (used to indicate
advanced settings) were being hidden due to ScrollWrapper's overflow
handling. This required both a fix for the visibility issue and an
improvement to the component structure.

### Changes
1. Keep scrolling logic of the MainNavigationDrawer and
SettingsNavigationDrawer in one place, and conditionally apply
`<StyledScrollableInnerContainer>` when isSettingsDrawer is true.

2. Fixed Yellow Dots Visibility
Added specific padding in NavigationDrawerScrollableContent to
accommodate yellow dots:
```
  padding-left: ${theme.spacing(5)}; // Space for yellow dots
  padding-right: ${theme.spacing(8)}; // Space for no-padding scroll
  ```
  
  This ensures the yellow dots are visible while maintaining proper scroll behavior

3. Improved Component Composition
Using proper component composition instead of passing components as props
Components are now composed in a more React-idiomatic way:
```
  <NavigationDrawer>
    <NavigationDrawerScrollableContent>
      <SettingsNavigationDrawerItems />
    </NavigationDrawerScrollableContent>
    <NavigationDrawerFixedContent>
      <AdvancedSettingsToggle />
    </NavigationDrawerFixedContent>
  </NavigationDrawer>
  ```

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
nitin
2025-04-10 17:35:10 +05:30
committed by GitHub
parent 41674129c3
commit db88c93eff
15 changed files with 342 additions and 267 deletions

View File

@ -1,19 +1,7 @@
import { useRecoilState, useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { SupportDropdown } from '@/support/components/SupportDropdown';
import {
NavigationDrawer,
NavigationDrawerProps,
} from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
import { MainNavigationDrawerItems } from '@/navigation/components/MainNavigationDrawerItems';
import { useLingui } from '@lingui/react/macro';
import { AdvancedSettingsToggle } from 'twenty-ui/navigation';
import { MainNavigationDrawer } from '@/navigation/components/MainNavigationDrawer';
import { SettingsNavigationDrawer } from '@/navigation/components/SettingsNavigationDrawer';
export type AppNavigationDrawerProps = {
className?: string;
@ -24,38 +12,9 @@ export const AppNavigationDrawer = ({
}: AppNavigationDrawerProps) => {
const isSettingsDrawer = useIsSettingsDrawer();
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const [isAdvancedModeEnabled, setIsAdvancedModeEnabled] = useRecoilState(
isAdvancedModeEnabledState,
);
const { t } = useLingui();
const drawerProps: NavigationDrawerProps = isSettingsDrawer
? {
title: t`Exit Settings`,
children: <SettingsNavigationDrawerItems />,
footer: (
<AdvancedSettingsToggle
isAdvancedModeEnabled={isAdvancedModeEnabled}
setIsAdvancedModeEnabled={setIsAdvancedModeEnabled}
label={t`Advanced:`}
/>
),
}
: {
title: currentWorkspace?.displayName ?? '',
children: <MainNavigationDrawerItems />,
footer: <SupportDropdown />,
};
return (
<NavigationDrawer
className={className}
title={drawerProps.title}
footer={drawerProps.footer}
>
{drawerProps.children}
</NavigationDrawer>
return isSettingsDrawer ? (
<SettingsNavigationDrawer className={className} />
) : (
<MainNavigationDrawer className={className} />
);
};

View File

@ -0,0 +1,32 @@
import { useRecoilValue } from 'recoil';
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
import { MainNavigationDrawerFixedItems } from '@/navigation/components/MainNavigationDrawerFixedItems';
import { MainNavigationDrawerScrollableItems } from '@/navigation/components/MainNavigationDrawerScrollableItems';
import { SupportDropdown } from '@/support/components/SupportDropdown';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
import { NavigationDrawerScrollableContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerScrollableContent';
export const MainNavigationDrawer = ({ className }: { className?: string }) => {
const currentWorkspace = useRecoilValue(currentWorkspaceState);
return (
<NavigationDrawer
className={className}
title={currentWorkspace?.displayName ?? ''}
>
<NavigationDrawerFixedContent>
<MainNavigationDrawerFixedItems />
</NavigationDrawerFixedContent>
<NavigationDrawerScrollableContent>
<MainNavigationDrawerScrollableItems />
</NavigationDrawerScrollableContent>
<NavigationDrawerFixedContent>
<SupportDropdown />
</NavigationDrawerFixedContent>
</NavigationDrawer>
);
};

View File

@ -0,0 +1,52 @@
import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu';
import { SettingsPath } from '@/types/SettingsPath';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useLingui } from '@lingui/react/macro';
import { useLocation } from 'react-router-dom';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { IconSearch, IconSettings } from 'twenty-ui/display';
import { useIsMobile } from 'twenty-ui/utilities';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
export const MainNavigationDrawerFixedItems = () => {
const isMobile = useIsMobile();
const location = useLocation();
const setNavigationMemorizedUrl = useSetRecoilState(
navigationMemorizedUrlState,
);
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useRecoilState(isNavigationDrawerExpandedState);
const setNavigationDrawerExpandedMemorized = useSetRecoilState(
navigationDrawerExpandedMemorizedState,
);
const { t } = useLingui();
const { openRecordsSearchPage } = useOpenRecordsSearchPageInCommandMenu();
return (
!isMobile && (
<>
<NavigationDrawerItem
label={t`Search`}
Icon={IconSearch}
onClick={openRecordsSearchPage}
keyboard={['/']}
/>
<NavigationDrawerItem
label={t`Settings`}
to={getSettingsPath(SettingsPath.ProfilePage)}
onClick={() => {
setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
setIsNavigationDrawerExpanded(true);
setNavigationMemorizedUrl(location.pathname + location.search);
}}
Icon={IconSettings}
/>
</>
)
);
};

View File

@ -1,82 +0,0 @@
import { useLocation } from 'react-router-dom';
import { useRecoilState, useSetRecoilState } from 'recoil';
import { useOpenRecordsSearchPageInCommandMenu } from '@/command-menu/hooks/useOpenRecordsSearchPageInCommandMenu';
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
import { WorkspaceFavorites } from '@/favorites/components/WorkspaceFavorites';
import { NavigationDrawerOpenedSection } from '@/object-metadata/components/NavigationDrawerOpenedSection';
import { RemoteNavigationDrawerSection } from '@/object-metadata/components/RemoteNavigationDrawerSection';
import { SettingsPath } from '@/types/SettingsPath';
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { isNavigationDrawerExpandedState } from '@/ui/navigation/states/isNavigationDrawerExpanded';
import { navigationDrawerExpandedMemorizedState } from '@/ui/navigation/states/navigationDrawerExpandedMemorizedState';
import { navigationMemorizedUrlState } from '@/ui/navigation/states/navigationMemorizedUrlState';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import { IconSearch, IconSettings } from 'twenty-ui/display';
const StyledMainSection = styled(NavigationDrawerSection)`
min-height: fit-content;
`;
const StyledInnerContainer = styled.div`
height: 100%;
width: 100%;
`;
export const MainNavigationDrawerItems = () => {
const isMobile = useIsMobile();
const location = useLocation();
const setNavigationMemorizedUrl = useSetRecoilState(
navigationMemorizedUrlState,
);
const [isNavigationDrawerExpanded, setIsNavigationDrawerExpanded] =
useRecoilState(isNavigationDrawerExpandedState);
const setNavigationDrawerExpandedMemorized = useSetRecoilState(
navigationDrawerExpandedMemorizedState,
);
const { t } = useLingui();
const { openRecordsSearchPage } = useOpenRecordsSearchPageInCommandMenu();
return (
<>
{!isMobile && (
<StyledMainSection>
<NavigationDrawerItem
label={t`Search`}
Icon={IconSearch}
onClick={openRecordsSearchPage}
keyboard={['/']}
/>
<NavigationDrawerItem
label={t`Settings`}
to={getSettingsPath(SettingsPath.ProfilePage)}
onClick={() => {
setNavigationDrawerExpandedMemorized(isNavigationDrawerExpanded);
setIsNavigationDrawerExpanded(true);
setNavigationMemorizedUrl(location.pathname + location.search);
}}
Icon={IconSettings}
/>
</StyledMainSection>
)}
<ScrollWrapper
componentInstanceId={`scroll-wrapper-navigation-drawer`}
defaultEnableXScroll={false}
>
<StyledInnerContainer>
<NavigationDrawerOpenedSection />
<CurrentWorkspaceMemberFavoritesFolders />
<WorkspaceFavorites />
<RemoteNavigationDrawerSection />
</StyledInnerContainer>
</ScrollWrapper>
</>
);
};

View File

@ -0,0 +1,22 @@
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
import { WorkspaceFavorites } from '@/favorites/components/WorkspaceFavorites';
import { NavigationDrawerOpenedSection } from '@/object-metadata/components/NavigationDrawerOpenedSection';
import { RemoteNavigationDrawerSection } from '@/object-metadata/components/RemoteNavigationDrawerSection';
import styled from '@emotion/styled';
const StyledScrollableItemsContainer = styled.div`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.spacing(3)};
`;
export const MainNavigationDrawerScrollableItems = () => {
return (
<StyledScrollableItemsContainer>
<NavigationDrawerOpenedSection />
<CurrentWorkspaceMemberFavoritesFolders />
<WorkspaceFavorites />
<RemoteNavigationDrawerSection />
</StyledScrollableItemsContainer>
);
};

View File

@ -0,0 +1,35 @@
import { SettingsNavigationDrawerItems } from '@/settings/components/SettingsNavigationDrawerItems';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
import { NavigationDrawerScrollableContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerScrollableContent';
import { isAdvancedModeEnabledState } from '@/ui/navigation/navigation-drawer/states/isAdvancedModeEnabledState';
import { useLingui } from '@lingui/react/macro';
import { useRecoilState } from 'recoil';
import { AdvancedSettingsToggle } from 'twenty-ui/navigation';
export const SettingsNavigationDrawer = ({
className,
}: {
className?: string;
}) => {
const { t } = useLingui();
const [isAdvancedModeEnabled, setIsAdvancedModeEnabled] = useRecoilState(
isAdvancedModeEnabledState,
);
return (
<NavigationDrawer className={className} title={t`Exit Settings`}>
<NavigationDrawerScrollableContent>
<SettingsNavigationDrawerItems />
</NavigationDrawerScrollableContent>
<NavigationDrawerFixedContent>
<AdvancedSettingsToggle
isAdvancedModeEnabled={isAdvancedModeEnabled}
setIsAdvancedModeEnabled={setIsAdvancedModeEnabled}
label={t`Advanced:`}
/>
</NavigationDrawerFixedContent>
</NavigationDrawer>
);
};

View File

@ -9,16 +9,9 @@ import { NavigationDrawerItemGroup } from '@/ui/navigation/navigation-drawer/com
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled';
import { matchPath, resolvePath, useLocation } from 'react-router-dom';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
const StyledInnerContainer = styled.div`
height: 100%;
width: 100%;
`;
export const SettingsNavigationDrawerItems = () => {
const settingsNavigationItems: SettingsNavigationSection[] =
useSettingsNavigationItems();
@ -41,86 +34,81 @@ export const SettingsNavigationDrawerItems = () => {
};
return (
<ScrollWrapper
componentInstanceId={`scroll-wrapper-settings-navigation-drawer`}
defaultEnableXScroll={false}
>
<StyledInnerContainer>
{settingsNavigationItems.map((section) => {
const allItemsHidden = section.items.every((item) => item.isHidden);
if (allItemsHidden) {
return null;
}
<>
{settingsNavigationItems.map((section) => {
const allItemsHidden = section.items.every((item) => item.isHidden);
if (allItemsHidden) {
return null;
}
return (
<NavigationDrawerSection key={section.label}>
{section.isAdvanced ? (
<AdvancedSettingsWrapper hideDot>
<NavigationDrawerSectionTitle label={section.label} />
</AdvancedSettingsWrapper>
) : (
return (
<NavigationDrawerSection key={section.label}>
{section.isAdvanced ? (
<AdvancedSettingsWrapper hideDot>
<NavigationDrawerSectionTitle label={section.label} />
)}
{section.items.map((item, index) => {
const subItems = item.subItems;
if (Array.isArray(subItems) && subItems.length > 0) {
const selectedSubItemIndex =
getSelectedIndexForSubItems(subItems);
</AdvancedSettingsWrapper>
) : (
<NavigationDrawerSectionTitle label={section.label} />
)}
{section.items.map((item, index) => {
const subItems = item.subItems;
if (Array.isArray(subItems) && subItems.length > 0) {
const selectedSubItemIndex =
getSelectedIndexForSubItems(subItems);
return (
<NavigationDrawerItemGroup
key={item.path || `group-${index}`}
>
return (
<NavigationDrawerItemGroup
key={item.path || `group-${index}`}
>
<SettingsNavigationDrawerItem
item={item}
subItemState={
item.indentationLevel
? getNavigationSubItemLeftAdornment({
arrayLength: section.items.length,
index,
selectedIndex: selectedSubItemIndex,
})
: undefined
}
/>
{subItems.map((subItem, subIndex) => (
<SettingsNavigationDrawerItem
item={item}
key={subItem.path || `subitem-${subIndex}`}
item={subItem}
subItemState={
item.indentationLevel
subItem.indentationLevel
? getNavigationSubItemLeftAdornment({
arrayLength: section.items.length,
index,
arrayLength: subItems.length,
index: subIndex,
selectedIndex: selectedSubItemIndex,
})
: undefined
}
/>
{subItems.map((subItem, subIndex) => (
<SettingsNavigationDrawerItem
key={subItem.path || `subitem-${subIndex}`}
item={subItem}
subItemState={
subItem.indentationLevel
? getNavigationSubItemLeftAdornment({
arrayLength: subItems.length,
index: subIndex,
selectedIndex: selectedSubItemIndex,
})
: undefined
}
/>
))}
</NavigationDrawerItemGroup>
);
}
return (
<SettingsNavigationDrawerItem
key={item.path || `item-${index}`}
item={item}
subItemState={
item.indentationLevel
? getNavigationSubItemLeftAdornment({
arrayLength: section.items.length,
index,
selectedIndex: index,
})
: undefined
}
/>
))}
</NavigationDrawerItemGroup>
);
})}
</NavigationDrawerSection>
);
})}
</StyledInnerContainer>
</ScrollWrapper>
}
return (
<SettingsNavigationDrawerItem
key={item.path || `item-${index}`}
item={item}
subItemState={
item.indentationLevel
? getNavigationSubItemLeftAdornment({
arrayLength: section.items.length,
index,
selectedIndex: index,
})
: undefined
}
/>
);
})}
</NavigationDrawerSection>
);
})}
</>
);
};

View File

@ -1,5 +1,6 @@
import { SupportDropdown } from '@/support/components/SupportDropdown';
import { NavigationDrawer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawer';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
import { NavigationDrawerSectionForObjectMetadataItems } from '@/object-metadata/components/NavigationDrawerSectionForObjectMetadataItems';
import { SettingsPath } from '@/types/SettingsPath';
@ -35,8 +36,8 @@ export const SignInAppNavigationDrawerMock = ({
const isMobile = useIsMobile();
const { t } = useLingui();
const children = (
<>
return (
<NavigationDrawer className={className} title={DEFAULT_WORKSPACE_NAME}>
{!isMobile && (
<StyledMainSection>
<NavigationDrawerItem
@ -60,18 +61,9 @@ export const SignInAppNavigationDrawerMock = ({
WORKSPACE_FAVORITES.includes(item.nameSingular),
)}
/>
</>
);
const footer = <SupportDropdown />;
return (
<NavigationDrawer
className={className}
footer={footer}
title={DEFAULT_WORKSPACE_NAME}
>
{children}
<NavigationDrawerFixedContent>
<SupportDropdown />
</NavigationDrawerFixedContent>
</NavigationDrawer>
);
};

View File

@ -84,7 +84,7 @@ export const DefaultLayout = () => {
? (windowsWidth -
(OBJECT_SETTINGS_WIDTH +
NAV_DRAWER_WIDTHS.menu.desktop.expanded +
64)) /
76)) /
2
: 0,
}}

View File

@ -4,26 +4,24 @@ import { motion } from 'framer-motion';
import { ReactNode, useState } from 'react';
import { useRecoilValue } from 'recoil';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { NAV_DRAWER_WIDTHS } from '@/ui/navigation/navigation-drawer/constants/NavDrawerWidths';
import { useIsMobile } from '@/ui/utilities/responsive/hooks/useIsMobile';
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import { isNavigationDrawerExpandedState } from '../../states/isNavigationDrawerExpanded';
import { NavigationDrawerBackButton } from './NavigationDrawerBackButton';
import { NavigationDrawerHeader } from './NavigationDrawerHeader';
import { MOBILE_VIEWPORT } from 'twenty-ui/theme';
export type NavigationDrawerProps = {
children: ReactNode;
children?: ReactNode;
className?: string;
footer?: ReactNode;
title: string;
};
const StyledAnimatedContainer = styled(motion.div)<{ isSettings?: boolean }>`
const StyledAnimatedContainer = styled(motion.div)`
max-height: 100vh;
overflow: ${({ isSettings }) => (isSettings ? 'visible' : 'hidden')};
overflow: hidden;
`;
const StyledContainer = styled.div<{
@ -33,35 +31,26 @@ const StyledContainer = styled.div<{
box-sizing: border-box;
display: flex;
flex-direction: column;
width: ${NAV_DRAWER_WIDTHS.menu.desktop.expanded}px;
width: ${({ isSettings }) =>
isSettings ? '100%' : NAV_DRAWER_WIDTHS.menu.desktop.expanded + 'px'};
gap: ${({ theme }) => theme.spacing(3)};
height: 100%;
padding: ${({ theme, isSettings, isMobile }) =>
isSettings
? isMobile
? theme.spacing(3, 8)
: theme.spacing(3, 8, 4, 0)
: theme.spacing(3, 2, 4)};
padding-right: 0px;
? theme.spacing(3, 0, 0, 8)
: theme.spacing(3, 0, 4, 0)
: theme.spacing(3, 0, 4, 2)};
@media (max-width: ${MOBILE_VIEWPORT}px) {
width: 100%;
padding-left: 20px;
padding-right: 20px;
padding-left: ${({ theme }) => theme.spacing(5)};
padding-right: ${({ theme }) => theme.spacing(5)};
}
`;
const StyledItemsContainer = styled.div<{ isSettings?: boolean }>`
display: flex;
flex-direction: column;
margin-bottom: auto;
overflow: hidden;
flex: 1;
`;
export const NavigationDrawer = ({
children,
className,
footer,
title,
}: NavigationDrawerProps) => {
const [isHovered, setIsHovered] = useState(false);
@ -99,7 +88,6 @@ export const NavigationDrawer = ({
initial={false}
animate={navigationDrawerAnimate}
transition={{ duration: theme.animation.duration.normal }}
isSettings={isSettingsDrawer}
>
<StyledContainer
isSettings={isSettingsDrawer}
@ -112,10 +100,8 @@ export const NavigationDrawer = ({
) : (
<NavigationDrawerHeader showCollapseButton={isHovered} />
)}
<StyledItemsContainer isSettings={isSettingsDrawer}>
{children}
</StyledItemsContainer>
<NavigationDrawerSection>{footer}</NavigationDrawerSection>
{children}
</StyledContainer>
</StyledAnimatedContainer>
);

View File

@ -39,6 +39,7 @@ const StyledContainer = styled.div`
flex-direction: row;
height: ${({ theme }) => theme.spacing(8)};
justify-content: space-between;
padding-left: ${({ theme }) => theme.spacing(5)};
`;
export const NavigationDrawerBackButton = ({

View File

@ -0,0 +1,33 @@
import { ReactNode } from 'react';
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
import styled from '@emotion/styled';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledFixedContainer = styled.div<{
isSettings?: boolean;
isMobile?: boolean;
}>`
${({ isSettings, theme, isMobile }) =>
isSettings
? `
padding-left: ${theme.spacing(5)};
padding-right: ${isMobile ? theme.spacing(5) : theme.spacing(8)};
`
: ''}
`;
export const NavigationDrawerFixedContent = ({
children,
}: {
children: ReactNode;
}) => {
const isSettingsDrawer = useIsSettingsDrawer();
const isMobile = useIsMobile();
return (
<StyledFixedContainer isSettings={isSettingsDrawer} isMobile={isMobile}>
<NavigationDrawerSection>{children}</NavigationDrawerSection>
</StyledFixedContainer>
);
};

View File

@ -0,0 +1,48 @@
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled';
import { ReactNode } from 'react';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledItemsContainer = styled.div`
display: flex;
flex-direction: column;
margin-bottom: auto;
overflow: hidden;
flex: 1;
`;
const StyledScrollableInnerContainer = styled.div<{ isMobile?: boolean }>`
height: 100%;
padding-left: ${({ theme }) => theme.spacing(5)};
padding-right: ${({ theme, isMobile }) =>
isMobile ? theme.spacing(5) : theme.spacing(8)};
`;
export const NavigationDrawerScrollableContent = ({
children,
}: {
children: ReactNode;
}) => {
const isSettingsDrawer = useIsSettingsDrawer();
const isMobile = useIsMobile();
return (
<ScrollWrapper
componentInstanceId={`scroll-wrapper-${
isSettingsDrawer ? 'settings-' : ''
}navigation-drawer`}
defaultEnableXScroll={false}
>
<StyledItemsContainer>
{isSettingsDrawer ? (
<StyledScrollableInnerContainer isMobile={isMobile}>
{children}
</StyledScrollableInnerContainer>
) : (
<>{children}</>
)}
</StyledItemsContainer>
</ScrollWrapper>
);
};

View File

@ -1,21 +1,22 @@
import { useIsSettingsDrawer } from '@/navigation/hooks/useIsSettingsDrawer';
import styled from '@emotion/styled';
import { useIsMobile } from 'twenty-ui/utilities';
const StyledSection = styled.div`
const StyledSection = styled.div<{ isSettingsDrawer?: boolean }>`
margin-bottom: ${({ theme, isSettingsDrawer }) =>
isSettingsDrawer ? theme.spacing(3) : 0};
width: 100%;
margin-bottom: ${({ theme }) => theme.spacing(3)};
flex-shrink: 1;
`;
const StyledSectionInnerContainerMinusScrollPadding = styled.div<{
isMobile: boolean;
isSettingsDrawer: boolean;
}>`
display: flex;
flex-direction: column;
gap: ${({ theme }) => theme.betweenSiblingsGap};
width: calc(
100% - ${({ isMobile, theme }) => (isMobile ? 0 : theme.spacing(2))}
);
width: ${({ isMobile, theme, isSettingsDrawer }) =>
`calc(100% - ${isMobile || isSettingsDrawer ? 0 : theme.spacing(2)})`};
`;
export const NavigationDrawerSection = ({
@ -24,9 +25,13 @@ export const NavigationDrawerSection = ({
children: React.ReactNode;
}) => {
const isMobile = useIsMobile();
const isSettingsDrawer = useIsSettingsDrawer();
return (
<StyledSection>
<StyledSectionInnerContainerMinusScrollPadding isMobile={isMobile}>
<StyledSection isSettingsDrawer={isSettingsDrawer}>
<StyledSectionInnerContainerMinusScrollPadding
isMobile={isMobile}
isSettingsDrawer={isSettingsDrawer}
>
{children}
</StyledSectionInnerContainerMinusScrollPadding>
</StyledSection>

View File

@ -16,16 +16,8 @@ import { generatedMockObjectMetadataItems } from '~/testing/mock-data/generatedM
import { mockedWorkspaceMemberData } from '~/testing/mock-data/users';
import { CurrentWorkspaceMemberFavoritesFolders } from '@/favorites/components/CurrentWorkspaceMemberFavoritesFolders';
import { NavigationDrawerFixedContent } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerFixedContent';
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import jsonPage from '../../../../../../../package.json';
import { NavigationDrawer } from '../NavigationDrawer';
import { NavigationDrawerItem } from '../NavigationDrawerItem';
import { NavigationDrawerItemGroup } from '../NavigationDrawerItemGroup';
import { NavigationDrawerSection } from '../NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '../NavigationDrawerSectionTitle';
import { GithubVersionLink } from 'twenty-ui/navigation';
import {
IconAt,
IconBell,
@ -42,7 +34,16 @@ import {
IconUserCircle,
IconUsers,
} from 'twenty-ui/display';
import { GithubVersionLink } from 'twenty-ui/navigation';
import { getOsControlSymbol } from 'twenty-ui/utilities';
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
import jsonPage from '../../../../../../../package.json';
import { NavigationDrawer } from '../NavigationDrawer';
import { NavigationDrawerItem } from '../NavigationDrawerItem';
import { NavigationDrawerItemGroup } from '../NavigationDrawerItemGroup';
import { NavigationDrawerSection } from '../NavigationDrawerSection';
import { NavigationDrawerSectionTitle } from '../NavigationDrawerSectionTitle';
const meta: Meta<typeof NavigationDrawer> = {
title: 'UI/Navigation/NavigationDrawer/NavigationDrawer',
@ -71,7 +72,7 @@ const meta: Meta<typeof NavigationDrawer> = {
layout: 'fullscreen',
msw: graphqlMocks,
},
argTypes: { children: { control: false }, footer: { control: false } },
argTypes: { children: { control: false } },
};
export default meta;
@ -79,6 +80,7 @@ type Story = StoryObj<typeof NavigationDrawer>;
export const Default: Story = {
args: {
title: 'Default',
children: (
<>
<NavigationDrawerSection>
@ -121,7 +123,6 @@ export const Default: Story = {
</NavigationDrawerSection>
</>
),
footer: null,
},
play: async () => {
const canvas = within(document.body);
@ -191,9 +192,12 @@ export const Settings: Story = {
Icon={IconServer}
/>
</NavigationDrawerSection>
<NavigationDrawerFixedContent>
<GithubVersionLink version={jsonPage.version} />
</NavigationDrawerFixedContent>
</>
),
footer: <GithubVersionLink version={jsonPage.version} />,
},
play: async () => {
const canvas = within(document.body);