Skip to content

Commit

Permalink
Dark mode (#15)
Browse files Browse the repository at this point in the history
* dark mode wip

* override button label text to be white in dark mode

* fixup tests, modularize store and themes

* fix theme in titlebar

* Revert "fix theme in titlebar"

This reverts commit 5c4420c.

* fix themes better
  • Loading branch information
chrisdopuch authored Jan 16, 2019
1 parent 0f68054 commit 62f0de3
Show file tree
Hide file tree
Showing 22 changed files with 275 additions and 170 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"dependencies": {
"@material-ui/core": "^3.2.2",
"lodash": "^4.17.11",
"material-ui": "^0.20.2",
"react": "16.7.0-alpha.2",
"react-dom": "16.7.0-alpha.2",
"react-ga": "^2.5.3",
Expand Down
20 changes: 3 additions & 17 deletions src/components/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
import { Theme } from '@material-ui/core';
import { shallow } from 'enzyme';
import React from 'react';
import { App, Projects } from './App';
import { App } from './App';

const appTheme = ({
breakpoints: {
up: jest.fn(),
},
spacing: {
unit: 1,
},
typography: {
useNextVariants: true,
},
zIndex: {
drawer: 1,
},
} as unknown) as Theme;
jest.mock('react-hookstore', () => ({ useStore: jest.fn(() => [{ isDarkMode: false }]) }));
jest.mock('../themes', () => ({ darkTheme: {}, appTheme: {} }));

function getDefaultProps() {
return {
Expand All @@ -25,7 +12,6 @@ function getDefaultProps() {
root: 'root',
toolbar: 'toolbar',
},
theme: appTheme,
title: 'test',
};
}
Expand Down
66 changes: 26 additions & 40 deletions src/components/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
import CssBaseline from '@material-ui/core/CssBaseline';
import {
createMuiTheme,
createStyles,
MuiThemeProvider,
Theme,
WithStyles,
withStyles,
} from '@material-ui/core/styles';
import { createStyles, MuiThemeProvider, Theme, WithStyles, withStyles } from '@material-ui/core/styles';
import CameraIcon from '@material-ui/icons/Camera';
import HelpIcon from '@material-ui/icons/Help';
import HomeIcon from '@material-ui/icons/Home';
import React from 'react';
import { BrowserRouter as Router } from 'react-router-dom';
import { Route } from 'react-router-dom';
import { useStore } from 'react-hookstore';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { IAppStore } from '../store';
import { appTheme, darkTheme } from '../themes';
import NavDrawer from './NavDrawer';
import About from './pages/About';
import Gallery from './pages/Gallery';
Expand Down Expand Up @@ -40,40 +35,31 @@ const stylesDeclarations = (theme: Theme) => {
});
};

export const appTheme = createMuiTheme({
typography: {
useNextVariants: true,
},
});

interface IAppProps extends WithStyles<typeof stylesDeclarations, true> {
title: string;
}

export class App extends React.Component<IAppProps> {
public render() {
const { classes, title, theme } = this.props;
export const App: React.SFC<IAppProps> = (props) => {
const { classes, title } = props;
const [appStore, _setStore]: [IAppStore, (s: IAppStore) => void] = useStore('appStore');

return (
<React.Fragment>
<CssBaseline />
<MuiThemeProvider theme={theme}>
<Router>
<div className={classes.root}>
<TitleBar title={title} />
<NavDrawer items={navBarItems} />
<main className={classes.content}>
<div className={classes.toolbar} />
{navBarItems.map(({ to, page, isRouteExact }) => {
return <Route path={to} component={page} exact={isRouteExact} key={to} />;
})}
</main>
</div>
</Router>
</MuiThemeProvider>
</React.Fragment>
);
}
}
return (
<MuiThemeProvider theme={appStore.isDarkMode ? darkTheme : appTheme}>
<CssBaseline />
<Router>
<div className={classes.root}>
<TitleBar title={title} />
<NavDrawer items={navBarItems} />
<main className={classes.content}>
<div className={classes.toolbar} />
{navBarItems.map(({ to, page, isRouteExact }) => {
return <Route path={to} component={page} exact={isRouteExact} key={to} />;
})}
</main>
</div>
</Router>
</MuiThemeProvider>
);
};

export default withStyles(stylesDeclarations, { withTheme: true })(App);
27 changes: 24 additions & 3 deletions src/components/DrawerContent.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Switch } from '@material-ui/core';
import Divider from '@material-ui/core/Divider';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
Expand All @@ -9,9 +10,16 @@ import React, { ComponentType, SFC } from 'react';
import { useStore } from 'react-hookstore';
import { Link } from 'react-router-dom';
import { Follow } from 'react-twitter-widgets';
import { IAppStore, isDarkModeKey } from '../store';

const stylesDeclarations = (theme: Theme) => {
return createStyles({
darkMode: {
paddingLeft: 0,
},
listItemIcon: {
marginRight: 6,
},
social: {
margin: '20px 10px',
textAlign: 'left',
Expand All @@ -33,7 +41,7 @@ interface IDrawerContentProps extends WithStyles<typeof stylesDeclarations> {

export const DrawerContent: SFC<IDrawerContentProps> = (props) => {
const { classes, items } = props;
const setisMobileDrawerOpen = useStore('appStore')[1];
const [appStore, setStore]: [IAppStore, (s: IAppStore) => void] = useStore('appStore');

return (
<div>
Expand All @@ -44,21 +52,34 @@ export const DrawerContent: SFC<IDrawerContentProps> = (props) => {
const linkProps = { to } as any;
return (
<ListItem
onClick={() => setisMobileDrawerOpen(false)} // tslint:disable-line
onClick={() => setStore(Object.assign({}, appStore, { isNavDrawerOpen: false }))}
button={true}
key={label}
component={Link}
{...linkProps}
>
<ListItemIcon>
<Icon />
<Icon className={classes.listItemIcon} />
</ListItemIcon>
<ListItemText primary={label} />
</ListItem>
);
})}
</List>
<Divider />
<ListItem className={classes.darkMode}>
<Switch
checked={appStore.isDarkMode}
onChange={(e) => {
const isDarkMode = e.target.checked;
localStorage.setItem(isDarkModeKey, isDarkMode.toString());
return setStore(Object.assign({}, appStore, { isDarkMode }));
}}
color="primary"
/>
<ListItemText primary="Dark Mode" />
</ListItem>
<Divider />
<div className={classes.social}>
<div className={classes.twitter}>
<Follow username="chrisdopuch" />
Expand Down
10 changes: 5 additions & 5 deletions src/components/NavDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/s
import { SvgIconProps } from '@material-ui/core/SvgIcon';
import React, { ComponentType, SFC } from 'react';
import { useStore } from 'react-hookstore';
import { IAppStore } from '../store';
import DrawerContent from './DrawerContent';

const drawerWidth = 240;
Expand Down Expand Up @@ -32,22 +33,21 @@ export interface INavDrawerProps extends WithStyles<typeof stylesDeclarations, t

export const NavDrawer: SFC<INavDrawerProps> = (props) => {
const { classes, theme, items } = props;
const [isMobileDrawerOpen, setisMobileDrawerOpen] = useStore('appStore');
const [appStore, setStore]: [IAppStore, (s: IAppStore) => void] = useStore('appStore');

return (
<nav className={classes.drawer}>
{/* The implementation can be swap with js to avoid SEO duplication of links. */}
<Hidden mdUp={true} implementation="css">
<Drawer
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={isMobileDrawerOpen}
onClose={() => setisMobileDrawerOpen(false)} // tslint:disable-line
open={appStore.isNavDrawerOpen}
onClose={() => setStore(Object.assign({}, appStore, { isNavDrawerOpen: false }))}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
keepMounted: true,
}}
>
<DrawerContent items={items} />
Expand Down
16 changes: 9 additions & 7 deletions src/components/TitleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Typography from '@material-ui/core/Typography';
import MenuIcon from '@material-ui/icons/Menu';
import React, { SFC } from 'react';
import { useStore } from 'react-hookstore';
import { appTheme } from './App';
import { IAppStore } from '../store';

const stylesDeclarations = (theme: Theme) => {
return createStyles({
Expand All @@ -28,15 +28,21 @@ interface ITitleBarProps extends WithStyles<typeof stylesDeclarations, true> {

export const TitleBar: SFC<ITitleBarProps> = (props) => {
const { classes, title } = props;
const [isMobileDrawerOpen, setIsMobileDrawerOpen] = useStore('appStore');
const [appStore, setStore]: [IAppStore, (s: IAppStore) => void] = useStore('appStore');

return (
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={() => setIsMobileDrawerOpen(!isMobileDrawerOpen)} // tslint:disable-line
onClick={() =>
setStore(
Object.assign({}, appStore, {
isNavDrawerOpen: !appStore.isNavDrawerOpen,
})
)
}
className={classes.menuButton}
>
<MenuIcon />
Expand All @@ -49,8 +55,4 @@ export const TitleBar: SFC<ITitleBarProps> = (props) => {
);
};

TitleBar.defaultProps = {
theme: appTheme,
};

export default withStyles(stylesDeclarations, { withTheme: true })(TitleBar);
Loading

0 comments on commit 62f0de3

Please sign in to comment.