Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[docs] Add density guide to customizations #16410

Merged
merged 25 commits into from
Jul 9, 2019
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
fee8986
[docs] Use dense theme
eps1lon Jun 28, 2019
7e6267f
Reduce button size
eps1lon Jun 28, 2019
70f020d
Reach 48dp touch hit target
eps1lon Jul 1, 2019
31793de
Add merge strategy for theme decorators
eps1lon Jul 1, 2019
2c6551d
[Button] Default to small in a dense theme
eps1lon Jul 2, 2019
8d29725
[FilledInput] Use dense margin in dense theme
eps1lon Jul 2, 2019
647a1d9
[FormControl] Use dense margin in a dense theme
eps1lon Jul 2, 2019
94e97d1
[FormHelperText] Use dense margin in a dense theme
eps1lon Jul 2, 2019
be939e4
[Fab] Use small size in a dense theme
eps1lon Jul 4, 2019
2811761
[IconButton] Use small size in a dense theme
eps1lon Jul 4, 2019
ed6d009
[InputBase] Use dense margin in a dense theme
eps1lon Jul 4, 2019
c6ba368
[InputLabel] Use dense margin in a dense theme
eps1lon Jul 4, 2019
12cc5a4
[ListItem] Use dense in a dense theme
eps1lon Jul 4, 2019
1e8ec2f
[OutlinedInput] Use dense margin in a dense theme
eps1lon Jul 4, 2019
1ade881
[Table] Use small size in a dense theme
eps1lon Jul 4, 2019
aa3e106
[Toolbar] Use dense variant in a dense theme
eps1lon Jul 4, 2019
5f26208
[docs] Update dense theme
eps1lon Jul 4, 2019
d57e523
[docs] Update API docs regarding density
eps1lon Jul 4, 2019
8c477f0
Fancy dense switch [skip ci]
eps1lon Jul 4, 2019
0f3484b
New draft for density guide
eps1lon Jul 5, 2019
906ab37
Test that density priority is props > context > theme
eps1lon Jul 5, 2019
534251f
Exclude density customization
eps1lon Jul 5, 2019
b9657d6
Remove theme.dense
eps1lon Jul 8, 2019
31d518e
Apply suggestions from Matt's code review
eps1lon Jul 8, 2019
90fdb4f
Apply suggestions from code review
eps1lon Jul 8, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/modules/components/AppWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { StylesProvider, jssPreset } from '@material-ui/styles';
import { Provider as ThemeProvider } from 'docs/src/modules/components/ThemeContext';
import { ThemeProvider } from 'docs/src/modules/components/ThemeContext';
import { getCookie } from 'docs/src/modules/utils/helpers';
import { ACTION_TYPES, CODE_VARIANTS } from 'docs/src/modules/constants';
import { create } from 'jss';
Expand Down
150 changes: 124 additions & 26 deletions docs/src/modules/components/ThemeContext.js
Original file line number Diff line number Diff line change
@@ -1,37 +1,131 @@
import React from 'react';
import PropTypes from 'prop-types';
import { ThemeProvider } from '@material-ui/styles';
import { ThemeProvider as MuiThemeProvider } from '@material-ui/styles';
import { createMuiTheme, darken } from '@material-ui/core/styles';
import useMediaQuery from '@material-ui/core/useMediaQuery';
import { blue, pink } from '@material-ui/core/colors';
import { getCookie } from 'docs/src/modules/utils/helpers';
import { darkTheme, setPrismTheme } from 'docs/src/modules/components/prism';
import deepmerge from 'deepmerge';

export const themeColor = blue[700];

const themeInitialOptions = {
dense: false,
direction: 'ltr',
paletteColors: {},
spacing: 8, // spacing unit
};

/**
* @typedef {import('@material-ui/core/src/styles/createMuiTheme').ThemeOptions} ThemeOptions
*
*
* @param {ThemeOptions} themeOptions
* @returns {ThemeOptions}
*/
function usingHighDensity(themeOptions) {
return deepmerge(themeOptions, {
props: {
MuiButton: {
size: 'small',
},
MuiFilledInput: {
margin: 'dense',
},
MuiFormControl: {
margin: 'dense',
},
MuiFormHelperText: {
margin: 'dense',
},
MuiIconButton: {
size: 'small',
},
MuiInputBase: {
margin: 'dense',
},
MuiInputLabel: {
margin: 'dense',
},
MuiListItem: {
dense: true,
},
MuiOutlinedInput: {
margin: 'dense',
},
MuiFab: {
size: 'small',
},
MuiTable: {
size: 'small',
},
MuiTextField: {
margin: 'dense',
},
MuiToolbar: {
variant: 'dense',
},
},
overrides: {
MuiIconButton: {
sizeSmall: {
// minimal touch target hit spacing
marginLeft: 4,
marginRight: 4,
padding: 12,
},
},
},
});
}

function usingIdentity(themeOptions) {
return themeOptions;
}

export const DispatchContext = React.createContext(() => {
throw new Error('Forgot to wrap component in ThemeContext.Provider');
});

const useEnhancedEffect = typeof window === 'undefined' ? React.useEffect : React.useLayoutEffect;

export function Provider(props) {
export function ThemeProvider(props) {
const { children } = props;

const [themeOptions, dispatch] = React.useReducer((state, action) => {
switch (action.type) {
case 'RESET_COLORS':
case 'SET_SPACING':
return {
...state,
spacing: action.payload,
};
case 'INCREASE_SPACING': {
return {
...state,
spacing: state.spacing + 1,
};
}
case 'DECREASE_SPACING': {
return {
...state,
spacing: state.spacing - 1,
};
}
case 'SET_DENSE':
return {
...state,
dense: action.payload,
};
case 'RESET_DENSITY':
return {
...state,
paletteColors: themeInitialOptions.paletteColors,
dense: themeInitialOptions.dense,
spacing: themeInitialOptions.spacing,
};
case 'CHANGE':
return {
...state,
paletteType: action.payload.paletteType || state.paletteType,
direction: action.payload.direction || state.direction,
paletteColors: action.payload.paletteColors || state.paletteColors,
Expand All @@ -43,7 +137,7 @@ export function Provider(props) {

const prefersDarkMode = useMediaQuery('@media (prefers-color-scheme: dark)');
const preferredType = prefersDarkMode ? 'dark' : 'light';
const { direction, paletteColors, paletteType = preferredType } = themeOptions;
const { dense, direction, paletteColors, paletteType = preferredType, spacing } = themeOptions;

React.useEffect(() => {
setPrismTheme(darkTheme);
Expand Down Expand Up @@ -71,25 +165,29 @@ export function Provider(props) {
}, [direction]);

const theme = React.useMemo(() => {
const nextTheme = createMuiTheme({
direction,
nprogress: {
color: paletteType === 'light' ? '#000' : '#fff',
},
palette: {
primary: {
main: paletteType === 'light' ? blue[700] : blue[200],
const themeDecorator = dense ? usingHighDensity : usingIdentity;
const nextTheme = createMuiTheme(
themeDecorator({
direction,
nprogress: {
color: paletteType === 'light' ? '#000' : '#fff',
},
secondary: {
main: paletteType === 'light' ? darken(pink.A400, 0.1) : pink[200],
palette: {
primary: {
main: paletteType === 'light' ? blue[700] : blue[200],
},
secondary: {
main: paletteType === 'light' ? darken(pink.A400, 0.1) : pink[200],
},
type: paletteType,
background: {
default: paletteType === 'light' ? '#fff' : '#121212',
},
...paletteColors,
},
type: paletteType,
background: {
default: paletteType === 'light' ? '#fff' : '#121212',
},
...paletteColors,
},
});
spacing,
}),
);

nextTheme.palette.background.level2 =
paletteType === 'light' ? nextTheme.palette.grey[100] : '#333';
Expand All @@ -98,7 +196,7 @@ export function Provider(props) {
paletteType === 'light' ? '#fff' : nextTheme.palette.grey[900];

return nextTheme;
}, [direction, paletteColors, paletteType]);
}, [dense, direction, paletteColors, paletteType, spacing]);

React.useEffect(() => {
// Expose the theme as a global variable so people can play with it.
Expand All @@ -108,13 +206,13 @@ export function Provider(props) {
}, [theme]);

return (
<ThemeProvider theme={theme}>
<MuiThemeProvider theme={theme}>
<DispatchContext.Provider value={dispatch}>{children}</DispatchContext.Provider>
</ThemeProvider>
</MuiThemeProvider>
);
}

Provider.propTypes = {
ThemeProvider.propTypes = {
children: PropTypes.node,
};

Expand Down
1 change: 1 addition & 0 deletions docs/src/pages.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ const pages = [
{ pathname: '/customization/typography' },
{ pathname: '/customization/spacing' },
{ pathname: '/customization/breakpoints' },
{ pathname: '/customization/density' },
{ pathname: '/customization/z-index', title: 'z-index' },
{ pathname: '/customization/globals' },
],
Expand Down
94 changes: 94 additions & 0 deletions docs/src/pages/customization/density/DensityTool.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import React from 'react';
import { useTheme } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Input from '@material-ui/core/Input';
import Typography from '@material-ui/core/Typography';
import Button from '@material-ui/core/Button';
import IconButton from '@material-ui/core/IconButton';
import FormControlLabel from '@material-ui/core/FormControlLabel';
import Switch from '@material-ui/core/Switch';
import { DispatchContext } from 'docs/src/modules/components/ThemeContext';
import IncreaseIcon from '@material-ui/icons/AddCircleOutline';
import DecreaseIcon from '@material-ui/icons/RemoveCircleOutline';
import { useSelector } from 'react-redux';

const minSpacing = 0;
const maxSpacing = 20;

export default function DensityTool() {
const dispatch = React.useContext(DispatchContext);
function handleDensityChange(event) {
dispatch({ type: 'SET_DENSE', payload: event.target.checked });
}

function handleSpacingChange(event, value) {
dispatch({ type: 'SET_SPACING', payload: value || +event.target.value });
}

function increaseSpacing() {
dispatch({ type: 'INCREASE_SPACING' });
}

function decreaseSpacing() {
dispatch({ type: 'DECREASE_SPACING' });
}

function resetDensity() {
dispatch({ type: 'RESET_DENSITY' });
}

const theme = useTheme();
const spacingUnit = theme.spacing(1);

const t = useSelector(state => state.options.t);

return (
<Grid container spacing={2}>
<Grid container item>
<FormControlLabel
control={
<Switch
checked={theme.dense}
onChange={handleDensityChange}
value="dense"
color="secondary"
/>
}
label={t('useHighDensity')}
/>
</Grid>
<Grid container item alignItems="center" spacing={2}>
<Grid item>
<Typography id="input-slider" gutterBottom>
{t('spacingUnit')}
</Typography>
</Grid>
<Grid item>
<IconButton aria-label={t('increaseSpacing')} onClick={decreaseSpacing}>
<DecreaseIcon />
</IconButton>
<Input
value={spacingUnit}
margin="dense"
onChange={handleSpacingChange}
inputProps={{
step: 1,
min: minSpacing,
max: maxSpacing,
type: 'number',
'aria-labelledby': 'input-slider',
}}
/>
<IconButton aria-label={t('decreaseSpacing')} onClick={increaseSpacing}>
<IncreaseIcon />
</IconButton>
</Grid>
</Grid>
<Grid item>
<Button color="primary" variant="contained" onClick={resetDensity}>
{t('resetDensity')}
</Button>
</Grid>
</Grid>
);
}
Loading