Skip to content

Commit ebb37ba

Browse files
committed
Display the ToggleThemeButton automatically
1 parent 0853cc2 commit ebb37ba

9 files changed

Lines changed: 132 additions & 163 deletions

File tree

docs/Admin.md

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -425,9 +425,12 @@ const App = () => (
425425

426426
## `darkTheme`
427427

428-
If you want to support both light and dark mode, you can provide a `darkTheme` in addition to the `theme` prop. The `darkTheme` will be used when the user's browser is in dark mode, or when the user manually switches to dark mode using [the `<ToggleThemeButton>` component](./ToggleThemeButton.md).
428+
If you want to support both light and dark mode, you can provide a `darkTheme` in addition to the `theme` prop. The app will use the `darkTheme` by default for users who prefer the dark mode at the OS level, and users will be able to switch from light to dark mode using a new app bar button leveraging [the `<ToggleThemeButton>` component](./ToggleThemeButton.md).
429429

430-
![Dark mode](./img/dark-theme.png)
430+
<video controls autoplay muted loop>
431+
<source src="./img/ToggleThemeButton.webm" type="video/webm"/>
432+
Your browser does not support the video tag.
433+
</video>
431434

432435
```jsx
433436
import { Admin } from 'react-admin';
@@ -444,37 +447,7 @@ const App = () => (
444447
);
445448
```
446449

447-
To let users change the theme manually, add a `<ToggleThemeButton>` component to the AppBar, and add that AppBar to a custom layout:
448-
449-
```jsx
450-
// in src/MyAppBar.js
451-
import { AppBar, TitlePortal, ToggleThemeButton } from 'react-admin';
452-
453-
export const MyAppBar = () => (
454-
<AppBar>
455-
<TitlePortal />
456-
<ToggleThemeButton />
457-
</AppBar>>
458-
);
459-
460-
// in src/App.js
461-
import { Admin, Layout } from 'react-admin';
462-
import { MyAppBar } from './MyAppBar';
463-
import { darkTheme, lightTheme } from './themes';
464-
465-
const MyLayout = (props) => <Layout {...props} appBar={MyAppBar} />;
466-
467-
const App = () => (
468-
<Admin
469-
dataProvider={dataProvider}
470-
layout={MyLayout}
471-
theme={lightTheme}
472-
darkTheme={darkTheme}
473-
>
474-
...
475-
</Admin>
476-
);
477-
```
450+
**Tip**: To disable OS preference detection and always use the light theme, see the [`defaultToLightTheme`](#defaulttolighttheme) prop.
478451

479452
## `defaultToLightTheme`
480453

docs/AppBar.md

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ By default, the `<AppBar>` component displays:
1717
- a hamburger icon to toggle the sidebar width,
1818
- the page title,
1919
- a button to change locales (if the application uses [i18n](./Translation.md)),
20+
- a button to change the theme (if the application uses a [dark theme](./Admin.md#darktheme)),
2021
- a loading indicator,
2122
- a button to display the user menu.
2223

@@ -74,28 +75,29 @@ Additional props are passed to [the underlying Material UI `<AppBar>` element](h
7475

7576
## `children`
7677

77-
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.
78+
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 settings button.
7879

7980
```jsx
8081
// in src/MyAppBar.js
81-
import {
82-
AppBar,
83-
TitlePortal,
84-
ToggleThemeButton,
85-
defaultTheme,
86-
} from 'react-admin';
82+
import { AppBar, TitlePortal } from 'react-admin';
83+
import SettingsIcon from '@mui/icons-material/Settings';
84+
import { IconButton } from '@mui/material';
8785

88-
const darkTheme = { palette: { mode: 'dark' } };
86+
const SettingsButton = () => (
87+
<IconButton color="inherit">
88+
<SettingsIcon />
89+
</IconButton>
90+
);
8991

9092
export const MyAppBar = () => (
9193
<AppBar>
9294
<TitlePortal />
93-
<ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
95+
<SettingsButton />
9496
</AppBar>
9597
);
9698
```
9799

98-
![App bar with a toggle theme button](./img/AppBar-children.png)
100+
![App bar with a settings button](./img/AppBar-children.png)
99101

100102
**Tip**: Whats the `<TitlePortal>`? It's a placeholder for the page title, that components in the page can fill using [the `<Title>` component](./Title.md). `<Title>` uses a [React Portal](https://reactjs.org/docs/portals.html) under the hood. `<TitlePortal>` takes all the available space in the app bar, so it "pushes" the following children to the right.
101103

@@ -166,7 +168,11 @@ To override the style of `<AppBar>` using the [Material UI style overrides](http
166168

167169
## `toolbar`
168170

169-
By default, the `<AppBar>` renders two buttons in addition to the user menu: the language menu and the refresh button.
171+
By default, the `<AppBar>` renders three buttons in addition to the user menu:
172+
173+
- the [language menu button](./LocalesMenuButton.md),
174+
- the [theme toggle button](./ToggleThemeButton.md),
175+
- and [the refresh button](./Buttons.md#refreshbutton).
170176

171177
If you want to reorder or remove these buttons, you can customize the toolbar by passing a `toolbar` prop.
172178

@@ -177,36 +183,37 @@ import {
177183
LocalesMenuButton,
178184
RefreshIconButton,
179185
ToggleThemeButton,
180-
defaultTheme,
181186
} from 'react-admin';
182187

183-
const darkTheme = {
184-
palette: { mode: 'dark' },
185-
};
186-
187188
export const MyAppBar = () => (
188189
<AppBar toolbar={
189190
<>
190191
<LocalesMenuButton />
191-
<ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
192+
<ToggleThemeButton />
192193
<RefreshIconButton />
193194
</>
194195
} />
195196
);
196197
```
197198

198-
**Tip**: If you only need to *add* buttons to the toolbar, you can pass them as children instead of overriding the entire toolbar.
199+
**Tip**: If you only need to *add* buttons to the toolbar, you can pass them as [children](#children) instead of overriding the entire toolbar.
199200

200201
```jsx
201202
// in src/MyAppBar.js
202-
import { AppBar, TitlePortal, ToggleThemeButton, defaultTheme } from 'react-admin';
203+
import { AppBar, TitlePortal } from 'react-admin';
204+
import SettingsIcon from '@mui/icons-material/Settings';
205+
import { IconButton } from '@mui/material';
203206

204-
const darkTheme = { palette: { mode: 'dark' } };
207+
const SettingsButton = () => (
208+
<IconButton color="inherit">
209+
<SettingsIcon />
210+
</IconButton>
211+
);
205212

206213
export const MyAppBar = () => (
207214
<AppBar>
208215
<TitlePortal />
209-
<ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
216+
<SettingsButton />
210217
</AppBar>
211218
);
212219
```
@@ -346,23 +353,24 @@ export const i18nProvider = {
346353

347354
To add buttons to the app bar, you can use the `<AppBar>` [`children` prop](#children).
348355

349-
For instance, to add `<ToggleThemeButton>`:
356+
For instance, to add a settings button:
350357

351358
```jsx
352359
// in src/MyAppBar.js
353-
import {
354-
AppBar,
355-
TitlePortal,
356-
ToggleThemeButton,
357-
defaultTheme,
358-
} from 'react-admin';
360+
import { AppBar, TitlePortal } from 'react-admin';
361+
import SettingsIcon from '@mui/icons-material/Settings';
362+
import { IconButton } from '@mui/material';
359363

360-
const darkTheme = { palette: { mode: 'dark' } };
364+
const SettingsButton = () => (
365+
<IconButton color="inherit">
366+
<SettingsIcon />
367+
</IconButton>
368+
);
361369

362370
export const MyAppBar = () => (
363371
<AppBar>
364372
<TitlePortal />
365-
<ToggleThemeButton lightTheme={defaultTheme} darkTheme={darkTheme} />
373+
<SettingsButton />
366374
</AppBar>
367375
);
368376
```

docs/Theming.md

Lines changed: 5 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -369,53 +369,13 @@ const App = () => (
369369

370370
It's a common practice to support both a light theme and a dark theme in an application, and let users choose which one they prefer.
371371

372-
React-admin's `<Admin>` component accepts a `darkTheme` mode in addition to the `theme` prop.
373-
374-
```jsx
375-
import { Admin, defaultTheme } from 'react-admin';
376-
377-
const lightTheme = defaultTheme;
378-
const darkTheme = { ...defaultTheme, palette: { mode: 'dark' } };
379-
380-
const App = () => (
381-
<Admin
382-
dataProvider={...}
383-
theme={lightTheme}
384-
darkTheme={darkTheme}
385-
>
386-
// ...
387-
</Admin>
388-
);
389-
```
390-
391-
With this setup, the default application theme will depend on the user's system settings. If the user has chosen a dark mode in their OS, react-admin will use the dark theme. Otherwise, it will use the light theme.
392-
393-
In addition, you can let users switch from one theme to the other: [the `<ToggleThemeButton>` component](./ToggleThemeButton.md) lets users switch from light to dark mode, and persists that choice by leveraging the [store](./Store.md).
394-
395372
<video controls autoplay muted loop>
396373
<source src="./img/ToggleThemeButton.webm" type="video/webm"/>
397374
Your browser does not support the video tag.
398375
</video>
399376

400-
Add the `<ToggleThemeButton>` to a custom App Bar:
401-
402-
```jsx
403-
// in src/MyLayout.jsx
404-
import * as React from 'react';
405-
import { Layout, AppBar, ToggleThemeButton, TitlePortal } from 'react-admin';
406-
import { Box, Typography } from '@mui/material';
407-
408-
const MyAppBar = () => (
409-
<AppBar>
410-
<TitlePortal />
411-
<ToggleThemeButton />
412-
</AppBar>
413-
);
414-
415-
const MyLayout = props => <Layout {...props} appBar={MyAppBar} />;
416-
```
417377

418-
Then, pass the custom layout to the `<Admin>` component:
378+
React-admin's `<Admin>` component accepts a `darkTheme` mode in addition to the `theme` prop.
419379

420380
```jsx
421381
import { Admin, defaultTheme } from 'react-admin';
@@ -428,13 +388,16 @@ const App = () => (
428388
dataProvider={...}
429389
theme={lightTheme}
430390
darkTheme={darkTheme}
431-
layout={MyLayout}
432391
>
433392
// ...
434393
</Admin>
435394
);
436395
```
437396

397+
With this setup, the default application theme depends on the user's system settings. If the user has chosen a dark mode in their OS, react-admin will use the dark theme. Otherwise, it will use the light theme.
398+
399+
In addition, users can switch from one theme to the other using [the `<ToggleThemeButton>` component](./ToggleThemeButton.md), which appears in the AppBar as soon as you define a `daerkTheme` prop.
400+
438401
## Changing the Theme Programmatically
439402

440403
React-admin provides the `useTheme` hook to read and update the theme programmatically. It uses the same syntax as `useState`. Its used internally by [the `<ToggleThemeButton>` component](./ToggleThemeButton.md).

docs/ToggleThemeButton.md

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ The `<ToggleThemeButton>` component lets users switch from light to dark mode, a
1212
Your browser does not support the video tag.
1313
</video>
1414

15+
It is enabled by default in the `<AppBar>` as soon as you define a dark theme via [the `<Admin darkTheme>` prop](./Admin.md#darktheme).
1516

1617
## Usage
1718

@@ -22,9 +23,8 @@ You can add the `<ToggleThemeButton>` to a custom App Bar:
2223
import { AppBar, TitlePortal, ToggleThemeButton } from 'react-admin';
2324

2425
export const MyAppBar = () => (
25-
<AppBar>
26+
<AppBar toolbar={<ToggleThemeButton />}>
2627
<TitlePortal />
27-
<ToggleThemeButton />
2828
</AppBar>>
2929
);
3030
```
@@ -51,6 +51,25 @@ const App = () => (
5151
```
5252
{% endraw %}
5353

54+
## Removing The Button From The AppBar
55+
56+
The `<ToggleThemeButton>` appears by default in the `<AppBar>` if the `<Admin darkTheme>` prop is defined. If you want to remove it, you need to set a custom [`<AppBar toolbar>` prop](./AppBar.md#toolbar):
57+
58+
```jsx
59+
// in src/MyAppBar.js
60+
import { AppBar, LocalesMenuButton, RefreshIconButton } from 'react-admin';
61+
62+
export const MyAppBar = () => (
63+
<AppBar toolbar={
64+
<>
65+
<LocalesMenuButton />
66+
{/* no ToggleThemeButton here */}
67+
<RefreshIconButton />
68+
</>
69+
} />
70+
);
71+
```
72+
5473
## Creating A Dark Theme
5574

5675
For this button to work, you must provide a dark theme to the `<Admin>` component. The `darkTheme` should be a JSON object that follows the [Material UI theme specification](https://material-ui.com/customization/theming/).

docs/img/AppBar-children.png

12.1 KB
Loading

examples/demo/src/layout/AppBar.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as React from 'react';
2-
import { AppBar, TitlePortal, ToggleThemeButton } from 'react-admin';
2+
import { AppBar, TitlePortal } from 'react-admin';
33
import { Box, useMediaQuery, Theme } from '@mui/material';
44

55
import Logo from './Logo';
@@ -13,7 +13,6 @@ const CustomAppBar = () => {
1313
<TitlePortal />
1414
{isLargeEnough && <Logo />}
1515
{isLargeEnough && <Box component="span" sx={{ flex: 1 }} />}
16-
<ToggleThemeButton />
1716
</AppBar>
1817
);
1918
};

packages/ra-ui-materialui/src/button/ToggleThemeButton.stories.tsx

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -85,48 +85,37 @@ const dataProvider = fakeRestDataProvider({
8585

8686
const history = createMemoryHistory({ initialEntries: ['/books'] });
8787

88-
const BookList = () => {
89-
return (
90-
<List>
91-
<Datagrid>
92-
<TextField source="id" />
93-
<TextField source="title" />
94-
<TextField source="author" />
95-
<TextField source="year" />
96-
</Datagrid>
97-
</List>
98-
);
99-
};
100-
101-
const MyAppBar = () => (
102-
<AppBar>
103-
<TitlePortal />
104-
<ToggleThemeButton />
105-
</AppBar>
88+
const BookList = () => (
89+
<List>
90+
<Datagrid>
91+
<TextField source="id" />
92+
<TextField source="title" />
93+
<TextField source="author" />
94+
<TextField source="year" />
95+
</Datagrid>
96+
</List>
10697
);
107-
const MyLayout = props => <Layout {...props} appBar={MyAppBar} />;
10898

10999
export const Basic = () => (
110100
<Admin
111101
dataProvider={dataProvider}
112102
history={history}
113-
layout={MyLayout}
114103
darkTheme={{ palette: { mode: 'dark' } }}
115104
>
116105
<Resource name="books" list={BookList} />
117106
</Admin>
118107
);
119108

120-
const LegacyAppBar = () => (
109+
const MyAppBar = () => (
121110
<AppBar>
122111
<TitlePortal />
123112
<ToggleThemeButton darkTheme={{ palette: { mode: 'dark' } }} />
124113
</AppBar>
125114
);
126-
const LegacyLayout = props => <Layout {...props} appBar={LegacyAppBar} />;
115+
const MyLayout = props => <Layout {...props} appBar={MyAppBar} />;
127116

128117
export const Legacy = () => (
129-
<Admin dataProvider={dataProvider} history={history} layout={LegacyLayout}>
118+
<Admin dataProvider={dataProvider} history={history} layout={MyLayout}>
130119
<Resource name="books" list={BookList} />
131120
</Admin>
132121
);

0 commit comments

Comments
 (0)