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

View File

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

View File

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