fix: scroll in sidenav (#24521)

* fix: scroll in sidenav

* fix: update snapshot
This commit is contained in:
Adrien L 2025-10-07 11:45:13 +02:00 committed by GitHub
parent 16642119fa
commit 3ae249212a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 796 additions and 766 deletions

View File

@ -7,6 +7,7 @@ import {
Typography, Typography,
IconButton, IconButton,
Badge, Badge,
ScrollArea,
} from '@strapi/design-system'; } from '@strapi/design-system';
import { ChevronDown, Plus } from '@strapi/icons'; import { ChevronDown, Plus } from '@strapi/icons';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
@ -349,8 +350,13 @@ const PageWrapper = styled(Box)`
} }
`; `;
const Content = ({ children }: { children: React.ReactNode }) => {
return <ScrollArea>{children}</ScrollArea>;
};
export const SubNav = { export const SubNav = {
Main, Main,
Content,
Header, Header,
Link, Link,
Sections, Sections,

View File

@ -1,4 +1,4 @@
import { Badge, Divider, ScrollArea } from '@strapi/design-system'; import { Badge, Divider } from '@strapi/design-system';
import { Lightning } from '@strapi/icons'; import { Lightning } from '@strapi/icons';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
@ -78,7 +78,7 @@ const SettingsNav = ({ isFullPage = false }: { isFullPage?: boolean }) => {
<Divider /> <Divider />
</> </>
)} )}
<ScrollArea> <SubNav.Content>
{isFullPage && <SubNav.Header label={label} />} {isFullPage && <SubNav.Header label={label} />}
<SubNav.Sections> <SubNav.Sections>
{sections.map((section) => ( {sections.map((section) => (
@ -124,7 +124,7 @@ const SettingsNav = ({ isFullPage = false }: { isFullPage?: boolean }) => {
</SubNav.Section> </SubNav.Section>
))} ))}
</SubNav.Sections> </SubNav.Sections>
</ScrollArea> </SubNav.Content>
</SubNav.Main> </SubNav.Main>
); );
}; };

View File

@ -1,15 +1,7 @@
import * as React from 'react'; import * as React from 'react';
import { useQueryParams, SubNav } from '@strapi/admin/strapi-admin'; import { useQueryParams, SubNav } from '@strapi/admin/strapi-admin';
import { import { Flex, Searchbar, useCollator, useFilter, Divider, Loader } from '@strapi/design-system';
Flex,
Searchbar,
useCollator,
useFilter,
Divider,
ScrollArea,
Loader,
} from '@strapi/design-system';
import { parse, stringify } from 'qs'; import { parse, stringify } from 'qs';
import { useIntl } from 'react-intl'; import { useIntl } from 'react-intl';
@ -143,7 +135,7 @@ const LeftMenu = ({ isFullPage = false }: { isFullPage?: boolean }) => {
<Divider /> <Divider />
</> </>
)} )}
<ScrollArea> <SubNav.Content>
{isFullPage && <SubNav.Header label={label} />} {isFullPage && <SubNav.Header label={label} />}
<Flex <Flex
paddingLeft={{ paddingLeft={{
@ -209,7 +201,7 @@ const LeftMenu = ({ isFullPage = false }: { isFullPage?: boolean }) => {
); );
})} })}
</SubNav.Sections> </SubNav.Sections>
</ScrollArea> </SubNav.Content>
</SubNav.Main> </SubNav.Main>
); );
}; };

View File

@ -99,167 +99,169 @@ export const ContentTypeBuilderNav = () => {
<SubNav.Main aria-label={pluginName}> <SubNav.Main aria-label={pluginName}>
<SubNav.Header label={pluginName} /> <SubNav.Header label={pluginName} />
<Divider background="neutral150" /> <Divider background="neutral150" />
<Flex padding={5} gap={3} direction={'column'} alignItems={'stretch'}> <SubNav.Content>
<tours.contentTypeBuilder.Save> <Flex padding={5} gap={3} direction={'column'} alignItems={'stretch'}>
<Flex gap={2}> <tours.contentTypeBuilder.Save>
<Button <Flex gap={2}>
flex={1} <Button
onClick={(e) => { flex={1}
e.preventDefault(); onClick={(e) => {
saveSchema(); e.preventDefault();
}} saveSchema();
type="submit" }}
disabled={!isModified || !isInDevelopmentMode} type="submit"
fullWidth disabled={!isModified || !isInDevelopmentMode}
size="S" fullWidth
>
{formatMessage({
id: 'global.save',
defaultMessage: 'Save',
})}
</Button>
<Menu.Root open={menuIsOpen} onOpenChange={setMenuIsOpen}>
<Menu.Trigger
size="S" size="S"
endIcon={null}
paddingTop="4px"
paddingLeft="7px"
paddingRight="7px"
variant="tertiary"
> >
<More fill="neutral500" aria-hidden focusable={false} /> {formatMessage({
<VisuallyHidden tag="span"> id: 'global.save',
{formatMessage({ defaultMessage: 'Save',
id: 'global.more.actions', })}
defaultMessage: 'More actions', </Button>
})} <Menu.Root open={menuIsOpen} onOpenChange={setMenuIsOpen}>
</VisuallyHidden> <Menu.Trigger
</Menu.Trigger> size="S"
<Menu.Content zIndex={1}> endIcon={null}
<Menu.Item paddingTop="4px"
disabled={!history.canUndo || !isInDevelopmentMode} paddingLeft="7px"
onSelect={undoHandler} paddingRight="7px"
startIcon={<ArrowCounterClockwise />} variant="tertiary"
> >
{formatMessage({ <More fill="neutral500" aria-hidden focusable={false} />
id: 'global.last-change.undo', <VisuallyHidden tag="span">
defaultMessage: 'Undo last change', {formatMessage({
})} id: 'global.more.actions',
</Menu.Item> defaultMessage: 'More actions',
<Menu.Item })}
disabled={!history.canRedo || !isInDevelopmentMode} </VisuallyHidden>
onSelect={redoHandler} </Menu.Trigger>
startIcon={<ArrowClockwise />} <Menu.Content zIndex={1}>
> <Menu.Item
{formatMessage({ disabled={!history.canUndo || !isInDevelopmentMode}
id: 'global.last-change.redo', onSelect={undoHandler}
defaultMessage: 'Redo last change', startIcon={<ArrowCounterClockwise />}
})} >
</Menu.Item> {formatMessage({
<Menu.Separator /> id: 'global.last-change.undo',
<DiscardAllMenuItem defaultMessage: 'Undo last change',
disabled={!history.canDiscardAll || !isInDevelopmentMode} })}
onSelect={discardHandler} </Menu.Item>
> <Menu.Item
<Flex gap={2}> disabled={!history.canRedo || !isInDevelopmentMode}
<Cross /> onSelect={redoHandler}
<Typography> startIcon={<ArrowClockwise />}
{formatMessage({ >
id: 'global.last-changes.discard', {formatMessage({
defaultMessage: 'Discard last changes', id: 'global.last-change.redo',
})} defaultMessage: 'Redo last change',
</Typography> })}
</Flex> </Menu.Item>
</DiscardAllMenuItem> <Menu.Separator />
</Menu.Content> <DiscardAllMenuItem
</Menu.Root> disabled={!history.canDiscardAll || !isInDevelopmentMode}
</Flex> onSelect={discardHandler}
</tours.contentTypeBuilder.Save> >
<Flex gap={2}>
<Cross />
<Typography>
{formatMessage({
id: 'global.last-changes.discard',
defaultMessage: 'Discard last changes',
})}
</Typography>
</Flex>
</DiscardAllMenuItem>
</Menu.Content>
</Menu.Root>
</Flex>
</tours.contentTypeBuilder.Save>
<Searchbar <Searchbar
value={search.value} value={search.value}
onChange={(e) => search.onChange(e.target.value)} onChange={(e) => search.onChange(e.target.value)}
onClear={() => search.onChange('')} onClear={() => search.onChange('')}
placeholder={formatMessage({ placeholder={formatMessage({
id: getTrad('search.placeholder'), id: getTrad('search.placeholder'),
defaultMessage: 'Search', defaultMessage: 'Search',
})} })}
size="S" size="S"
// eslint-disable-next-line react/no-children-prop // eslint-disable-next-line react/no-children-prop
children={undefined} children={undefined}
name={'search_contentType'} name={'search_contentType'}
clearLabel={formatMessage({ id: 'clearLabel', defaultMessage: 'Clear' })} clearLabel={formatMessage({ id: 'clearLabel', defaultMessage: 'Clear' })}
aria-label={formatMessage({ aria-label={formatMessage({
id: getTrad('search.placeholder'), id: getTrad('search.placeholder'),
defaultMessage: 'Search', defaultMessage: 'Search',
})} })}
/> />
</Flex> </Flex>
<SubNav.Sections> <SubNav.Sections>
{menu.map((section) => ( {menu.map((section) => (
<Fragment key={section.name}> <Fragment key={section.name}>
<SubNav.Section <SubNav.Section
label={formatMessage({ label={formatMessage({
id: section.title.id, id: section.title.id,
defaultMessage: section.title.defaultMessage, defaultMessage: section.title.defaultMessage,
})} })}
link={ link={
section.customLink && { section.customLink && {
label: formatMessage({ label: formatMessage({
id: section.customLink?.id, id: section.customLink?.id,
defaultMessage: section.customLink?.defaultMessage, defaultMessage: section.customLink?.defaultMessage,
}), }),
onClick: section.customLink?.onClick, onClick: section.customLink?.onClick,
}
} }
} sectionId={section.name}
sectionId={section.name} >
> {section.links.map((link) => {
{section.links.map((link) => { const linkLabel = formatMessage({ id: link.name, defaultMessage: link.title });
const linkLabel = formatMessage({ id: link.name, defaultMessage: link.title });
if ('links' in link) {
return (
<SubNav.SubSection key={link.name} label={link.title}>
{link.links.map((subLink) => {
const label = formatMessage({
id: subLink.name,
defaultMessage: subLink.title,
});
return (
<SubNav.Link
to={subLink.to}
key={subLink.name}
label={label}
endAction={
<Box tag="span" textAlign="center" width={'24px'}>
<Status status={subLink.status} />
</Box>
}
/>
);
})}
</SubNav.SubSection>
);
}
if ('links' in link) {
return ( return (
<SubNav.SubSection key={link.name} label={link.title}> <SubNav.Link
{link.links.map((subLink) => { to={link.to}
const label = formatMessage({ key={link.name}
id: subLink.name, label={linkLabel}
defaultMessage: subLink.title, endAction={
}); <Box tag="span" textAlign="center" width={'24px'}>
<Status status={link.status} />
return ( </Box>
<SubNav.Link }
to={subLink.to} />
key={subLink.name}
label={label}
endAction={
<Box tag="span" textAlign="center" width={'24px'}>
<Status status={subLink.status} />
</Box>
}
/>
);
})}
</SubNav.SubSection>
); );
} })}
</SubNav.Section>
return ( </Fragment>
<SubNav.Link ))}
to={link.to} </SubNav.Sections>
key={link.name} </SubNav.Content>
label={linkLabel}
endAction={
<Box tag="span" textAlign="center" width={'24px'}>
<Status status={link.status} />
</Box>
}
/>
);
})}
</SubNav.Section>
</Fragment>
))}
</SubNav.Sections>
<Dialog.Root <Dialog.Root
open={discardConfirmationModalIsOpen} open={discardConfirmationModalIsOpen}
onOpenChange={setDiscardConfirmationModalIsOpen} onOpenChange={setDiscardConfirmationModalIsOpen}