Skip to content

Commit

Permalink
Merge pull request #2777 from marmelab/appbar-children
Browse files Browse the repository at this point in the history
[RFR] Add the ability to customize AppBar content
  • Loading branch information
Gildas Garcia authored Jan 18, 2019
2 parents 43d5508 + 55f8094 commit 40ce5b7
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 106 deletions.
73 changes: 60 additions & 13 deletions docs/Theming.md
Original file line number Diff line number Diff line change
Expand Up @@ -550,30 +550,50 @@ const mapStateToProps = state => ({ isLoading: state.admin.loading > 0 });
export default connect(mapStateToProps, { setSidebarVisibility })(withStyles(styles)(MyLayout));
```

## Using a Custom AppBar
## Customizing the AppBar Content

By default, React-admin uses [Material_ui's `<AppBar>` component](https://material-ui.com/api/app-bar/) together with [react-headroom](https://github.com/KyleAMathews/react-headroom) to hide the `AppBar` on scroll.
By default, the react-admin `<AppBar>` component displays the page title. You can override this default by passing children to `<AppBar>` - they will replace the default title. And if you still want to include the page title, make sure you include an element with id `react-admin-title` in the top bar (this uses [React Portals](https://reactjs.org/docs/portals.html)).

You can create your own `AppBar` component to replace the react-admin one. For instance, to remove the "headroom" effect:
Here is an example customization for `<AppBar>` to include a company logo in the center of the page header:

```jsx
// in src/MyAppBar.js
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import React from 'react';
import { AppBar } from 'react-admin';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';

const MyAppBar = props => (
import Logo from './Logo';

const styles = {
title: {
flex: 1,
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
},
spacer: {
flex: 1,
},
};

const MyAppBar = withStyles(styles)(({ classes, ...props }) => (
<AppBar {...props}>
<Toolbar>
<Typography variant="title" id="react-admin-title" />
</Toolbar>
<Typography
variant="title"
color="inherit"
className={classes.title}
id="react-admin-title"
/>
<Logo />
<span className={classes.spacer} />
</AppBar>
);
));

export default MyAppBar;
```

To use this custom `AppBar` component, pass it as prop to a custom `Layout`, as explained below:
To use this custom `MyAppBar` component, pass it as prop to a custom `Layout`, as shown below:

```jsx
// in src/MyLayout.js
Expand All @@ -585,7 +605,7 @@ const MyLayout = (props) => <Layout {...props} appBar={MyAppBar} />;
export default MyLayout;
```

Then, use this layout in the `<Admin>` with the `applayout` prop:
Then, use this layout in the `<Admin>` with the `appLayout` prop:

```jsx
// in src/App.js
Expand All @@ -598,6 +618,33 @@ const App = () => (
);
```

![custom AppBar](./img/custom_appbar.png)

## Replacing The AppBar

For more drastic changes of the top component, you will probably want to create an `<AppBar>` from scratch instead of just passing children to react-admin's `<AppBar>`.

By default, React-admin uses [Material-ui's `<AppBar>` component](https://material-ui.com/api/app-bar/) together with [react-headroom](https://github.com/KyleAMathews/react-headroom) to hide the `AppBar` on scroll. Here is an example top bar rebuilt from scratch to remove the "headroom" effect:

```jsx
// in src/MyAppBar.js
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Typography from '@material-ui/core/Typography';

const MyAppBar = props => (
<AppBar {...props}>
<Toolbar>
<Typography variant="title" id="react-admin-title" />
</Toolbar>
</AppBar>
);

export default MyAppBar;
```

Take note that this uses *material-ui's `<AppBar>`* instead of *react-admin's `<AppBar>`*. To use this custom `AppBar` component, pass it as prop to a custom `Layout`, as explained in the previous section.

## Using a Custom Menu

By default, React-admin uses the list of `<Resource>` components passed as children of `<Admin>` to build a menu to each resource with a `list` component.
Expand Down Expand Up @@ -651,7 +698,7 @@ const MyLayout = (props) => <Layout {...props} menu={MyMenu} />;
export default MyLayout;
```

Then, use this layout in the `<Admin>` `applayout` prop:
Then, use this layout in the `<Admin>` `appLayout` prop:

```jsx
// in src/App.js
Expand Down
Binary file added docs/img/custom_appbar.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 2 additions & 4 deletions examples/demo/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@ import './App.css';
import authProvider from './authProvider';
import sagas from './sagas';
import themeReducer from './themeReducer';
import Login from './Login';
import Layout from './Layout';
import Menu from './Menu';
import { Login, Layout, Menu } from './layout';
import { Dashboard } from './dashboard';
import customRoutes from './routes';
import englishMessages from './i18n/en';
Expand Down Expand Up @@ -73,7 +71,7 @@ class App extends Component {

return (
<Admin
title="Posters Galore Admin"
title=""
dataProvider={dataProvider}
customReducers={{ theme: themeReducer }}
customSagas={sagas}
Expand Down
19 changes: 0 additions & 19 deletions examples/demo/src/AppBar.js

This file was deleted.

111 changes: 54 additions & 57 deletions examples/demo/src/dashboard/Dashboard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React, { Component, Fragment } from 'react';
import { GET_LIST, GET_MANY, Responsive, Title } from 'react-admin';
import React, { Component } from 'react';
import { GET_LIST, GET_MANY, Responsive } from 'react-admin';

import Welcome from './Welcome';
import MonthlyRevenue from './MonthlyRevenue';
Expand Down Expand Up @@ -148,31 +148,11 @@ class Dashboard extends Component {
revenue,
} = this.state;
return (
<Fragment>
<Title title="Posters Galore Admin" />
<Responsive
xsmall={
<div>
<div style={styles.flexColumn}>
<div style={{ marginBottom: '2em' }}>
<Welcome />
</div>
<div style={styles.flex}>
<MonthlyRevenue value={revenue} />
<NbNewOrders value={nbNewOrders} />
</div>
<div style={styles.singleCol}>
<PendingOrders
orders={pendingOrders}
customers={pendingOrdersCustomers}
/>
</div>
</div>
</div>
}
small={
<Responsive
xsmall={
<div>
<div style={styles.flexColumn}>
<div style={styles.singleCol}>
<div style={{ marginBottom: '2em' }}>
<Welcome />
</div>
<div style={styles.flex}>
Expand All @@ -186,41 +166,58 @@ class Dashboard extends Component {
/>
</div>
</div>
}
medium={
</div>
}
small={
<div style={styles.flexColumn}>
<div style={styles.singleCol}>
<Welcome />
</div>
<div style={styles.flex}>
<div style={styles.leftCol}>
<div style={styles.flex}>
<MonthlyRevenue value={revenue} />
<NbNewOrders value={nbNewOrders} />
</div>
<div style={styles.singleCol}>
<Welcome />
</div>
<div style={styles.singleCol}>
<PendingOrders
orders={pendingOrders}
customers={pendingOrdersCustomers}
/>
</div>
<MonthlyRevenue value={revenue} />
<NbNewOrders value={nbNewOrders} />
</div>
<div style={styles.singleCol}>
<PendingOrders
orders={pendingOrders}
customers={pendingOrdersCustomers}
/>
</div>
</div>
}
medium={
<div style={styles.flex}>
<div style={styles.leftCol}>
<div style={styles.flex}>
<MonthlyRevenue value={revenue} />
<NbNewOrders value={nbNewOrders} />
</div>
<div style={styles.singleCol}>
<Welcome />
</div>
<div style={styles.rightCol}>
<div style={styles.flex}>
<PendingReviews
nb={nbPendingReviews}
reviews={pendingReviews}
customers={pendingReviewsCustomers}
/>
<NewCustomers
nb={nbNewCustomers}
visitors={newCustomers}
/>
</div>
<div style={styles.singleCol}>
<PendingOrders
orders={pendingOrders}
customers={pendingOrdersCustomers}
/>
</div>
</div>
<div style={styles.rightCol}>
<div style={styles.flex}>
<PendingReviews
nb={nbPendingReviews}
reviews={pendingReviews}
customers={pendingReviewsCustomers}
/>
<NewCustomers
nb={nbNewCustomers}
visitors={newCustomers}
/>
</div>
</div>
}
/>
</Fragment>
</div>
}
/>
);
}
}
Expand Down
44 changes: 44 additions & 0 deletions examples/demo/src/layout/AppBar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import { AppBar, UserMenu, MenuItemLink, translate } from 'react-admin';
import Typography from '@material-ui/core/Typography';
import SettingsIcon from '@material-ui/icons/Settings';
import { withStyles } from '@material-ui/core/styles';

import Logo from './Logo';

const styles = {
title: {
flex: 1,
textOverflow: 'ellipsis',
whiteSpace: 'nowrap',
overflow: 'hidden',
},
spacer: {
flex: 1,
},
};

const CustomUserMenu = translate(({ translate, ...props }) => (
<UserMenu {...props}>
<MenuItemLink
to="/configuration"
primaryText={translate('pos.configuration')}
leftIcon={<SettingsIcon />}
/>
</UserMenu>
));

const CustomAppBar = withStyles(styles)(({ classes, ...props }) => (
<AppBar {...props} userMenu={<CustomUserMenu />}>
<Typography
variant="title"
color="inherit"
className={classes.title}
id="react-admin-title"
/>
<Logo />
<span className={classes.spacer} />
</AppBar>
));

export default CustomAppBar;
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import LockIcon from '@material-ui/icons/Lock';

import { Notification, translate, userLogin } from 'react-admin';

import { darkTheme, lightTheme } from './themes';
import { lightTheme } from './themes';

const styles = theme => ({
main: {
Expand Down
Loading

0 comments on commit 40ce5b7

Please sign in to comment.