Skip to content

Commit bb9c53c

Browse files
authored
Merge pull request #8856 from marmelab/appBarAlwaysOn
Fix AppBar with alwaysOn hides sidebar menu on scroll
2 parents c933ab5 + e331902 commit bb9c53c

File tree

7 files changed

+135
-30
lines changed

7 files changed

+135
-30
lines changed

docs/AppBar.md

-12
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ const App = () => (
6464

6565
| Prop | Required | Type | Default | Description |
6666
| ------------------- | -------- | -------------- | -------- | --------------------------------------------------- |
67-
| `alwaysOn` | Optional | `boolean` | - | When true, the app bar is always visible |
6867
| `children` | Optional | `ReactElement` | - | What to display in the central part of the app bar |
6968
| `color` | Optional | `string` | - | The background color of the app bar |
7069
| `sx` | Optional | `SxProps` | - | Style overrides, powered by MUI System |
@@ -73,17 +72,6 @@ const App = () => (
7372

7473
Additional props are passed to [the underlying MUI `<AppBar>` element](https://mui.com/material-ui/api/app-bar/).
7574

76-
## `alwaysOn`
77-
78-
By default, the app bar is hidden when the user scrolls down the page. This is useful to save space on small screens. But if you want to keep the app bar always visible, you can set the `alwaysOn` prop to `true`.
79-
80-
```jsx
81-
// in src/MyAppBar.js
82-
import { AppBar } from 'react-admin';
83-
84-
const MyAppBar = () => <AppBar alwaysOn />;
85-
```
86-
8775
## `children`
8876

8977
The `<AppBar>` component accepts a `children` prop, which is displayed in the central part of the app bar. This is useful to add buttons to the app bar, for instance, a light/dark theme switcher.

docs/Layout.md

+21-7
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,15 @@ const App = () => (
4545

4646
## Props
4747

48-
| Prop | Required | Type | Default | Description |
49-
| ----------- | -------- | ----------- | -------- | --------------------------------------------------------------------- |
50-
| `appBar` | Optional | `Component` | - | A React component rendered at the top of the layout |
51-
| `className` | Optional | `string` | - | Passed to the root `<div>` component |
52-
| `error` | Optional | `Component` | - | A React component rendered in the content area in case of error |
53-
| `menu` | Optional | `Component` | - | A React component rendered at the side of the screen |
54-
| `sx` | Optional | `SxProps` | - | Style overrides, powered by MUI System |
48+
| Prop | Required | Type | Default | Description |
49+
| ---------------- | -------- | ----------- | -------- | ----------------------------------------------------------------------- |
50+
| `appBar` | Optional | `Component` | - | A React component rendered at the top of the layout |
51+
| `appBarAlwaysOn` | Optional | `boolean` | - | When true, the app bar is always visible |
52+
| `className` | Optional | `string` | - | Passed to the root `<div>` component |
53+
| `error` | Optional | `Component` | - | A React component rendered in the content area in case of error |
54+
| `menu` | Optional | `Component` | - | A React component rendered at the side of the screen |
55+
| `sidebar` | Optional | `Component` | - | A React component responsible for rendering the menu (e.g. in a drawer) |
56+
| `sx` | Optional | `SxProps` | - | Style overrides, powered by MUI System |
5557

5658
React-admin injects more props at runtime based on the `<Admin>` props:
5759

@@ -114,6 +116,18 @@ export const MyAppBar = () => (
114116

115117
Check out the [`<AppBar>` documentation](./AppBar.md) for more information, and for instructions on building your own AppBar.
116118

119+
## `appBarAlwaysOn`
120+
121+
By default, the app bar is hidden when the user scrolls down the page. This is useful to save space on small screens. But if you want to keep the app bar always visible, you can set the `appBarAlwaysOn` prop to `true`.
122+
123+
```jsx
124+
// in src/MyLayout.js
125+
import * as React from 'react';
126+
import { Layout } from 'react-admin';
127+
128+
export const MyLayout = (props) => <Layout {...props} appBarAlwaysOn />;
129+
```
130+
117131
## `className`
118132

119133
`className` is passed to the root `<div>` component. It lets you style the layout with CSS - but the `sx` prop is preferred.

packages/ra-ui-materialui/src/layout/AppBar.stories.tsx

-6
Original file line numberDiff line numberDiff line change
@@ -74,12 +74,6 @@ export const Color = () => (
7474
</Wrapper>
7575
);
7676

77-
export const AlwaysOn = () => (
78-
<Wrapper>
79-
<AppBar alwaysOn />
80-
</Wrapper>
81-
);
82-
8377
export const Position = () => (
8478
<Wrapper>
8579
<AppBar position="sticky" />

packages/ra-ui-materialui/src/layout/AppBar.tsx

+5
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,11 @@ AppBar.propTypes = {
126126
const DefaultUserMenu = <UserMenu />;
127127

128128
export interface AppBarProps extends Omit<MuiAppBarProps, 'title'> {
129+
/**
130+
* This prop is injected by Layout. You should not use it directly unless
131+
* you are using a custom layout.
132+
* If you are using the default layout, use `<Layout appBarAlwaysOn>` instead.
133+
*/
129134
alwaysOn?: boolean;
130135
container?: React.ElementType<any>;
131136
/**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import {
2+
Box,
3+
ListItemIcon,
4+
ListItemText,
5+
MenuItem,
6+
MenuList,
7+
Skeleton,
8+
ThemeProvider,
9+
createTheme,
10+
} from '@mui/material';
11+
import DashboardIcon from '@mui/icons-material/Dashboard';
12+
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
13+
import PeopleIcon from '@mui/icons-material/People';
14+
import {
15+
AuthContext,
16+
PreferencesEditorContextProvider,
17+
StoreContextProvider,
18+
memoryStore,
19+
} from 'ra-core';
20+
import * as React from 'react';
21+
import { QueryClient, QueryClientProvider } from 'react-query';
22+
import { MemoryRouter } from 'react-router';
23+
24+
import { defaultTheme } from '../defaultTheme';
25+
import { Layout } from './Layout';
26+
import { Title } from './Title';
27+
28+
export default {
29+
title: 'ra-ui-materialui/layout/Layout',
30+
};
31+
32+
const Content = () => (
33+
<Box>
34+
<Skeleton
35+
variant="text"
36+
width="auto"
37+
sx={{ fontSize: '2rem', mx: 2 }}
38+
animation={false}
39+
/>
40+
<Skeleton
41+
variant="rectangular"
42+
width="auto"
43+
height={1500}
44+
sx={{ mx: 2 }}
45+
animation={false}
46+
/>
47+
</Box>
48+
);
49+
50+
const Wrapper = ({
51+
children = <Content />,
52+
theme = createTheme(defaultTheme),
53+
layout: LayoutProp = Layout,
54+
}) => (
55+
<MemoryRouter>
56+
<QueryClientProvider client={new QueryClient()}>
57+
<ThemeProvider theme={theme}>
58+
<StoreContextProvider value={memoryStore()}>
59+
<PreferencesEditorContextProvider>
60+
<AuthContext.Provider value={undefined as any}>
61+
<LayoutProp>
62+
{children}
63+
<Title title="React Admin" />
64+
</LayoutProp>
65+
</AuthContext.Provider>
66+
</PreferencesEditorContextProvider>
67+
</StoreContextProvider>
68+
</ThemeProvider>
69+
</QueryClientProvider>
70+
</MemoryRouter>
71+
);
72+
73+
const Menu = () => (
74+
<MenuList>
75+
<MenuItem>
76+
<ListItemIcon>
77+
<DashboardIcon />
78+
</ListItemIcon>
79+
<ListItemText>Dashboard</ListItemText>
80+
</MenuItem>
81+
<MenuItem>
82+
<ListItemIcon>
83+
<ShoppingCartIcon />
84+
</ListItemIcon>
85+
<ListItemText>Orders</ListItemText>
86+
</MenuItem>
87+
<MenuItem>
88+
<ListItemIcon>
89+
<PeopleIcon />
90+
</ListItemIcon>
91+
<ListItemText>Customers</ListItemText>
92+
</MenuItem>
93+
</MenuList>
94+
);
95+
96+
const BasicLayout = props => <Layout menu={Menu} {...props} />;
97+
export const Basic = () => <Wrapper layout={BasicLayout} />;
98+
99+
const AppBarAlwaysOnLayout = props => <BasicLayout appBarAlwaysOn {...props} />;
100+
export const AppBarAlwaysOn = () => <Wrapper layout={AppBarAlwaysOnLayout} />;

packages/ra-ui-materialui/src/layout/Layout.tsx

+4-2
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import { Inspector } from '../preferences';
2121
export const Layout = (props: LayoutProps) => {
2222
const {
2323
appBar: AppBar = DefaultAppBar,
24+
appBarAlwaysOn,
2425
children,
2526
className,
2627
dashboard,
@@ -42,9 +43,9 @@ export const Layout = (props: LayoutProps) => {
4243
<StyledLayout className={clsx('layout', className)} {...rest}>
4344
<SkipNavigationButton />
4445
<div className={LayoutClasses.appFrame}>
45-
<AppBar open={open} title={title} />
46+
<AppBar open={open} title={title} alwaysOn={appBarAlwaysOn} />
4647
<main className={LayoutClasses.contentWithSidebar}>
47-
<Sidebar>
48+
<Sidebar appBarAlwaysOn={appBarAlwaysOn}>
4849
<Menu hasDashboard={!!dashboard} />
4950
</Sidebar>
5051
<div id="main-content" className={LayoutClasses.content}>
@@ -74,6 +75,7 @@ export interface LayoutProps
7475
extends CoreLayoutProps,
7576
Omit<HtmlHTMLAttributes<HTMLDivElement>, 'title'> {
7677
appBar?: ComponentType<AppBarProps>;
78+
appBarAlwaysOn?: boolean;
7779
className?: string;
7880
error?: ComponentType<ErrorProps>;
7981
menu?: ComponentType<MenuProps>;

packages/ra-ui-materialui/src/layout/Sidebar.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { useLocale } from 'ra-core';
1515
import { useSidebarState } from './useSidebarState';
1616

1717
export const Sidebar = (props: SidebarProps) => {
18-
const { children, closedSize, size, ...rest } = props;
18+
const { appBarAlwaysOn, children, closedSize, size, ...rest } = props;
1919
const isXSmall = useMediaQuery<Theme>(theme =>
2020
theme.breakpoints.down('sm')
2121
);
@@ -41,7 +41,9 @@ export const Sidebar = (props: SidebarProps) => {
4141
open={open}
4242
onClose={toggleSidebar}
4343
classes={SidebarClasses}
44-
className={trigger ? SidebarClasses.appBarCollapsed : ''}
44+
className={
45+
trigger && !appBarAlwaysOn ? SidebarClasses.appBarCollapsed : ''
46+
}
4547
{...rest}
4648
>
4749
<div className={SidebarClasses.fixed}>{children}</div>
@@ -54,9 +56,9 @@ Sidebar.propTypes = {
5456
};
5557

5658
export interface SidebarProps extends DrawerProps {
59+
appBarAlwaysOn?: boolean;
5760
children: ReactElement;
5861
closedSize?: number;
59-
6062
size?: number;
6163
}
6264

0 commit comments

Comments
 (0)