diff --git a/.circleci/config.yml b/.circleci/config.yml
index 449a01f0733d8e..9dbb2a8876b6ad 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -1,7 +1,7 @@
defaults: &defaults
working_directory: /tmp/material-ui
docker:
- - image: circleci/node:9.10
+ - image: circleci/node:10.14
# CircleCI has disabled the cache across forks for security reasons.
# Following their official statement, it was a quick solution, they
# are working on providing this feature back with appropriate security measures.
@@ -153,7 +153,7 @@ jobs:
# This isn't user facing code.
# Let's take advantage of the most up to date node version.
docker:
- - image: circleci/node:9.10
+ - image: circleci/node:10.14
steps:
- checkout
- *restore_yarn_offline_mirror
@@ -199,7 +199,7 @@ jobs:
test_regressions:
<<: *defaults
docker:
- - image: circleci/node:9.10
+ - image: circleci/node:10.14
- image: selenium/standalone-chrome:3.11.0
steps:
- checkout
diff --git a/.size-limit.js b/.size-limit.js
index 3ba1462ac60606..7b4a3d8e0dc780 100644
--- a/.size-limit.js
+++ b/.size-limit.js
@@ -16,25 +16,32 @@ module.exports = [
name: 'The initial cost paid for using one component',
webpack: true,
path: 'packages/material-ui/build/Paper/index.js',
- limit: '18.5 KB',
+ limit: '18.6 KB',
},
{
name: 'The size of the @material-ui/core modules',
webpack: true,
path: 'packages/material-ui/build/index.js',
- limit: '94.8 KB',
+ limit: '95.3 KB',
},
{
name: 'The size of the @material-ui/styles modules',
webpack: true,
path: 'packages/material-ui-styles/build/index.js',
- limit: '14.9 KB',
+ limit: '15.0 KB',
+ },
+ {
+ // vs https://bundlephobia.com/result?p=react-popper
+ name: 'The size of the @material-ui/core/Popper component',
+ webpack: true,
+ path: 'packages/material-ui/build/Popper/index.js',
+ limit: '10.7 KB',
},
{
name: 'The main docs bundle',
webpack: false,
path: main.path,
- limit: '190 KB',
+ limit: '177 KB',
},
{
name: 'The docs home page',
diff --git a/BACKERS.md b/BACKERS.md
index 2bd7760ce3085b..4f1b3c60b5cf94 100644
--- a/BACKERS.md
+++ b/BACKERS.md
@@ -69,6 +69,7 @@ via [Patreon](https://www.patreon.com/oliviertassinari)
| Arvanitis Panagiotis | Jesse Weigel | Bogdan Mihai Nicolae | Dung Tran | Kyle Pennell |
| Kai Mit Pansen | Eric Nagy | Karens Grigorjancs | Mohamed Turco | Haroun Serang |
| Antonio Seveso | Ali Akhavan | Bruno Winck | Alessandro Annini | Victor Irzak |
+| Manuel U Grullon |
via [OpenCollective](https://opencollective.com/material-ui)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 964e78429705f8..b844e065296b9b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,219 @@
### [Versions](https://material-ui.com/versions/)
+## 3.6.2
+###### *Dec 9, 2018*
+
+Big thanks to the 20 contributors who made this release possible!
+
+Here are some highlights ✨:
+- 🎨 Add a new Onepirate theme demo (#13769) @oliviertassinari
+You can preview it following [this link](https://material-ui.com/premium-themes/paperbase/).
+- 📝 Add virtualized table demo (#13786) @joshwooding
+- 🚀 Avoid unnecessary Table re-rendering (#13832) @petrjaros
+- And many more 🐛 bug fixes and documentation improvements.
+
+### `@material-ui/core@v3.6.2`
+
+- [Tooltip] Supress warning if button is disabled and title is empty (#13785) @rfbotto
+- [Dialog] Warn if className in PaperProps is set (#13797) @eps1lon
+- [TextField] Fix textfield label position when empty (#13791) @Studio384
+- [Popper] Save 7 KB gzipped (for people only using it) (#13804) @oliviertassinari
+- [Modal] Handle modal mount interruption (#13778) @amensouissi
+- [Select] Make value prop required in TypeScript (#13810) @t49tran
+- [Popover] Fix onEntering event propagation (#13821) @ekoeditaa
+- [Input] Make CSS override a bit simpler (#13825) @euharrison
+- [LinearProgress] Add determinate and indeterminate classes to root element (#13828) @alxsnchez
+- [Select] Support native multiple value (#13830) @rfbotto
+- [BottomNavigation] Improve action padding (#13851) @rfbotto
+- [Dialog] Add dialog with close button to demos (#13845) @rfbotto
+- [Tabs] Reduce the bundle size (#13853) @oliviertassinari
+- [Dialog] Add missing TypeScript style rule (#13856) @garredow
+- [Table] Avoid unnecessary re-rendering (#13832) @petrjaros
+
+### `@material-ui/lab@v3.0.0-alpha.25`
+
+- [ToggleButtonGroup] Consider nullish instead of falsy value as no selected value (#13494) @ItamarShDev
+- [Slider] Update SliderClassKey types (#13826) @guiihlopes
+- [SpeedDialAction] Add TooltipClasses prop (#13848) @mbrookes
+- [ToggleButton] Change ToggleButtonGroup non-exclusive default value to an empty array (#13857) @joshwooding
+
+### `@material-ui/styles@v3.0.0-alpha.3`
+
+- [styles] Infer optional props argument for makeStyles in TypeScript (#13815) @oliviertassinari
+
+### Docs
+
+- [docs] Add @eps1lon to the team page (#13768) @oliviertassinari
+- [docs] Add a new onepirate theme (#13769) @oliviertassinari
+- [docs] Link tags HTML vs JSX (#13775) @benbowler
+- [docs] Missing text in docs (#13798) @Skn0tt
+- [docs] Add virtualized table demo (#13786) @joshwooding
+- [docs] Add OpenCollective gold sponsors manually (#13806) @mbrookes
+- [docs] Add example of globally disabling animations (#13805) @joshwooding
+- [docs] Fix KeyboardIcon import name (#13822) @bryantabaird
+- [docs] Force common hoist-non-react-statics version (#13818) @eps1lon
+- [docs] Improve the theme nesting documentation (#13843) @oliviertassinari
+- [docs] Add more details regarding installation of material-ui/styles (#13813) @wilcoschoneveld
+- [docs] Fix broken link anchor (#13862) @mvasin
+
+### Core
+
+- [typescript] Add test case for List type limitations (#13764) @eps1lon
+- [core] Remove unused lint directives (#13766) @eps1lon
+- [test] Fix running tests on Windows (#13852) @joshwooding
+- [core] Upgrade the dependencies (#13858) @oliviertassinari
+
+## 3.6.1
+###### *Dec 1, 2018*
+
+Big thanks to the 15 contributors who made this release possible!
+
+There are no fundamental changes in this version.
+It's a stability release after v3.6.0. It contains tons of bug fixes 🐛.
+
+### `@material-ui/core@v3.6.1`
+
+- [Dialog] Add xl maxWidth and demo component (#13694) @dispix
+- [Dialog] Add missing TypeScript style rule (ddfa8e0215bfe895efcb8da69f1ea3cc3b1370ff) @oliviertassinari
+- [ClickAwayListener] Ignore touchend after touchmove (#13689) @hsimah
+- [Tooltip] Hide native title when disableHoverListener is true (#13690) @joshwooding
+- [withTheme] Fix typography warning (#13707) @jmcpeak
+- [Fab] Add Fab type declaration to index and theme (#13715) @Naturalclar
+- [InputBase] Remove dead disableUnderline property (#13720) @PierreCapo
+- [FilledInput] Fix disableUnderline property (#13719) @ekoeditaa
+- [SwitchBase] Fix error not being thrown when controlled state is changed (#13726) @joshwooding
+- [TextField] Better support select object value (#13730) @yezhi780625
+- [TablePagination] Support native selection (#13737) @jsdev
+- [Modal] Fix concurrency regression (#13743) @oliviertassinari
+- [LinearProgress] Remove dead code (#13749) @ekoeditaa
+- [typescript] Add test case for FormControl type limitations (#13754) @eps1lon
+- [Popover] Handle resize close concurrency issue (#13758) @oliviertassinari
+- [Avatar] Remove truthiness check on childrenProp (#13759) @camilleryr
+
+### `@material-ui/styles@v3.0.0-alpha.2`
+
+- [styles] Add options definitions for makeStyles (#13721) @eps1lon
+- [styles] Loosen props consistency check in styled (#13755) @eps1lon
+
+### Docs
+
+- [docs] Add support for changing react version in codesandbox demos (#13686) @joshwooding
+- [CHANGELOG] Add deprecation notice for Divider (#13700) @eps1lon
+- [docs] Add notistack demo to the snackbar page (#13685) @iamhosseindhv
+- [docs] Remove Grid List dead code (#13731) @akhil-gautam
+- [docs] Reduce the no-results rate on Algolia (#13741) @oliviertassinari
+- [docs] Fix concurrency with Frame demos (#13747) @oliviertassinari
+
+### Core
+
+- [test] Correct the link to the example test (#13709) @mdcanham
+- [styles] Fix tslint false negative with outdated local builds (#13750) @eps1lon
+
+## 3.6.0
+###### *Nov 26, 2018*
+
+Big thanks to the 28 contributors who made this release possible!
+
+The last release was two weeks ago.
+Last weekend, we have missed the release train 🚃.
+As a consequence, this is a dense release.
+
+Here are some highlights ✨:
+- 🎨 Add a new Firebase theme demo (#13579) @siriwatknp.
+You can preview it following [this link](https://material-ui.com/premium-themes/paperbase/).
+- ⚛️ Introduce a new Fab component (#13573) @mbrookes.
+- ⛏ Fix more StrictMode warnings (#13590) @eps1lon.
+- And many more 🐛 bug fixes and 📝 documentation improvements.
+
+### `@material-ui/core@v3.6.0`
+
+#### Deprecations
+
+- [Fab] Extract from Button as new component (#13573) @mbrookes
+
+The floating action button doesn't share many styles with the default button component.
+We are extracting the variant into its own component.
+This way, we better isolate the concerns.
+We will remove the FAB styles from the button in v4, making the `Button` component more lightweight, a win for people overriding our styles.
+
+```diff
+-import Button from '@material-ui/core/Button';
++import Fab from '@material-ui/core/Fab';
+
+-
++
+
+-
++
+```
+
+- [Divider] Add support for middle divider by introducing a `variant` prop (#13574) @joshwooding
+
+We are introducing a new variant to the divider component: middle. Following our API guideline, we can no longer use a boolean property, it needs to be an enum, hence the introduction of the variant property.
+
+```diff
+import Divider from '@material-ui/core/Divider';
+
+-
++
+```
+
+#### Changes
+
+- [FormControlLabel] Fix documentation warnings (#13583) @dsbrutha777
+- [ExpansionPanelSummary] Fix event forwarding (#13582) @jmetev1
+- [Button] Move deprecated variants to the end of the list (#13584) @avetisk
+- [FormControl] Use stable context API (#13590) @eps1lon
+- [TablePagination] Improve TypeScript definition (#13601) @xiaoyu-tamu
+- [SwipeableDrawer] Add `SwipeAreaProps` property (#13592) @SerhiiBilyk
+- [ListItem] Add three-line support (#13553) @ntorion
+- [Grid] Fix the IE 11 issue in the demo (7d2070fb388295d38806ecc49717006f34393e74) @oliviertassinari
+- [Zoom] Correct transition delay value of the example (#13645) @t49tran
+- [Tabs] Improve the warning message (#13640) @oliviertassinari
+- [Grow] Condense the demo (#13665) @Thyix
+- [Tooltip] Fix the property forwarding priority (#13667) @oliviertassinari
+- [Modal] Fix the close jump on Windows (#13674) @oliviertassinari
+- [Select] Support object value (#13661) @yezhi780625
+- [Menu] Fix wrong condition (#13675) @dolezel
+
+### `@material-ui/lab@v3.0.0-alpha.24`
+
+- [Slider] Fix sticky slider when mousing off the window then back in (#13479) @gkjohnson
+- [Slider] Fix visual hover state on disabled slider (#13638) @eps1lon
+- [Slider] Add missing thumb TypeScript definition (#13650) @dhiroll
+
+### `@material-ui/styles@v3.0.0-alpha.1`
+
+- [styles] Add TypeScript declarations (#13612) @eps1lon
+
+### `@material-ui/docs@v3.0.0-alpha.8`
+
+- Fix the @material-ui/utils require error.
+
+### Docs
+
+- [docs] Add redirect rule for moved page layout examples (#13588) @mbrookes
+- [docs] Add the selfeducation.app showcase (#13620) @kulakowka
+- [docs] Warn about the Dynamic CSS alpha state (#13619) @WebDeg-Brian
+- [docs] Learn Material-UI (#13624) @oliviertassinari
+- [docs] Add a Firebase example in the premium-theme section (#13579) @siriwatknp
+- [docs] Increase clarity around the usage of font icons (#13628) @JosephMart
+- [docs] Add swimmy.io to showcase page (#13637) @uufish
+- [docs] Correct typo in comment of snackbar, children (#13651) @kobi
+- [docs] Improve Grid limitation description (#13668) @sshevlyagin
+- [docs] Fix theme menu link (#13669) @iamhosseindhv
+- [docs] Change "e; to ' (#13678) @wiktoriatomzik
+- [docs] Restructure the demo based on usage analytics (#13684) @oliviertassinari
+- [docs] Fix typo in URL (#13688) @Malvineous
+
+### Core
+
+- [core] Update dev dependencies (#13626) @oliviertassinari
+- [test] Fix codecov failing on merge commits (#13654) @eps1lon
+- [core] Make prettier run programmatically (#13621) @joshwooding
+- [test] Run unit/integration test on Chrome 41 (#13642) @eps1lon
+- [core] Move unit test commands to their package (#13604) @eps1lon
+
## 3.5.1
###### *Nov 13, 2018*
diff --git a/babel.config.js b/babel.config.js
index 99aa0b844a04a0..49cdb85a4e57f6 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -43,6 +43,7 @@ module.exports = {
'@babel/plugin-transform-object-assign',
'@babel/plugin-transform-runtime',
],
+ ignore: [/@babel[\\|/]runtime/],
env: {
test: {
sourceMaps: 'both',
@@ -130,7 +131,7 @@ module.exports = {
[
'transform-react-remove-prop-types',
{
- mode: 'wrap',
+ mode: 'unsafe-wrap',
},
],
],
@@ -146,7 +147,7 @@ module.exports = {
[
'transform-react-remove-prop-types',
{
- mode: 'wrap',
+ mode: 'unsafe-wrap',
},
],
],
@@ -162,7 +163,7 @@ module.exports = {
[
'transform-react-remove-prop-types',
{
- mode: 'wrap',
+ mode: 'unsafe-wrap',
},
],
],
diff --git a/docs/notifications.json b/docs/notifications.json
index a62a0a4bc7f6f1..275d13035c3d79 100644
--- a/docs/notifications.json
+++ b/docs/notifications.json
@@ -1,10 +1,6 @@
[
{
- "id": 27,
- "text": "You can follow us on Twitter to receive exclusive tips and updates about Material-UI and React ecosystem."
- },
- {
- "id": 30,
- "text": "Material-UI v3.5.1 is out 🎉. Please see the release notes ."
+ "id": 32,
+ "text": "Material-UI v3.6.2 is out 🎉. Please see the release notes ."
}
]
diff --git a/docs/scripts/buildApi.js b/docs/scripts/buildApi.js
index 5587df500379b8..3c4d96eded438e 100644
--- a/docs/scripts/buildApi.js
+++ b/docs/scripts/buildApi.js
@@ -134,7 +134,7 @@ function buildDocs(options) {
reactAPI.pagesMarkdown = pagesMarkdown;
reactAPI.src = src;
- // if (reactAPI.name !== 'Button') {
+ // if (reactAPI.name !== 'TableCell') {
// return;
// }
diff --git a/docs/src/modules/components/AppContent.js b/docs/src/modules/components/AppContent.js
index c9024c00304ade..ab3f29b418a469 100644
--- a/docs/src/modules/components/AppContent.js
+++ b/docs/src/modules/components/AppContent.js
@@ -14,12 +14,12 @@ const styles = theme => ({
[theme.breakpoints.up('sm')]: {
paddingLeft: theme.spacing.unit * 4,
paddingRight: theme.spacing.unit * 4,
- maxWidth: 'calc(100% - 162px)',
+ maxWidth: 'calc(100% - 167px)',
},
[theme.breakpoints.up('lg')]: {
paddingLeft: theme.spacing.unit * 5,
paddingRight: theme.spacing.unit * 9,
- maxWidth: 'calc(100% - 240px - 162px)',
+ maxWidth: 'calc(100% - 240px - 167px)',
},
},
});
diff --git a/docs/src/modules/components/AppFrame.js b/docs/src/modules/components/AppFrame.js
index b4a9a1228818c3..2b713a287b4e23 100644
--- a/docs/src/modules/components/AppFrame.js
+++ b/docs/src/modules/components/AppFrame.js
@@ -2,7 +2,6 @@ import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import compose from 'recompose/compose';
-import fromRenderProps from 'recompose/fromRenderProps';
import NProgress from 'nprogress';
import Router from 'next/router';
import { withStyles } from '@material-ui/core/styles';
@@ -23,13 +22,12 @@ import NProgressBar from '@material-ui/docs/NProgressBar';
import FormatTextdirectionLToR from '@material-ui/icons/FormatTextdirectionLToR';
import FormatTextdirectionRToL from '@material-ui/icons/FormatTextdirectionRToL';
import GithubIcon from '@material-ui/docs/svgIcons/GitHub';
-import PageContext from 'docs/src/modules/components/PageContext';
import Link from 'docs/src/modules/components/Link';
import AppDrawer from 'docs/src/modules/components/AppDrawer';
import AppSearch from 'docs/src/modules/components/AppSearch';
import Notifications from 'docs/src/modules/components/Notifications';
import PageTitle from 'docs/src/modules/components/PageTitle';
-import actionTypes from 'docs/src/modules/redux/actionTypes';
+import { ACTION_TYPES } from 'docs/src/modules/constants';
Router.onRouteChangeStart = () => {
NProgress.start();
@@ -102,8 +100,8 @@ class AppFrame extends React.Component {
this.setState({ languageMenu: null });
};
- handleLanguageMenuItemClick = lang => {
- if (lang !== this.props.userLanguage) {
+ handleLanguageMenuItemClick = lang => () => {
+ if (lang !== this.props.options.userLanguage) {
document.cookie = `lang=${lang};path=/;max-age=31536000`;
window.location.reload();
}
@@ -111,11 +109,11 @@ class AppFrame extends React.Component {
};
handleTogglePaletteType = () => {
- const paletteType = this.props.uiTheme.paletteType === 'light' ? 'dark' : 'light';
+ const paletteType = this.props.reduxTheme.paletteType === 'light' ? 'dark' : 'light';
document.cookie = `paletteType=${paletteType};path=/;max-age=31536000`;
this.props.dispatch({
- type: actionTypes.THEME_CHANGE_PALETTE_TYPE,
+ type: ACTION_TYPES.THEME_CHANGE,
payload: {
paletteType,
},
@@ -124,15 +122,15 @@ class AppFrame extends React.Component {
handleToggleDirection = () => {
this.props.dispatch({
- type: actionTypes.THEME_CHANGE_DIRECTION,
+ type: ACTION_TYPES.THEME_CHANGE,
payload: {
- direction: this.props.uiTheme.direction === 'ltr' ? 'rtl' : 'ltr',
+ direction: this.props.reduxTheme.direction === 'ltr' ? 'rtl' : 'ltr',
},
});
};
render() {
- const { children, classes, uiTheme, userLanguage } = this.props;
+ const { children, classes, reduxTheme, options } = this.props;
const { languageMenu } = this.state;
return (
@@ -191,14 +189,14 @@ class AppFrame extends React.Component {
onClose={this.handleLanguageMenuClose}
>
this.handleLanguageMenuItemClick('en')}
+ selected={options.userLanguage === 'en'}
+ onClick={this.handleLanguageMenuItemClick('en')}
>
English
this.handleLanguageMenuItemClick('zh')}
+ selected={options.userLanguage === 'zh'}
+ onClick={this.handleLanguageMenuItemClick('zh')}
>
中文
@@ -223,7 +221,7 @@ class AppFrame extends React.Component {
data-ga-event-category="AppBar"
data-ga-event-action="dark"
>
- {uiTheme.paletteType === 'light' ? (
+ {reduxTheme.paletteType === 'light' ? (
) : (
@@ -238,7 +236,7 @@ class AppFrame extends React.Component {
data-ga-event-category="AppBar"
data-ga-event-action="rtl"
>
- {uiTheme.direction === 'rtl' ? (
+ {reduxTheme.direction === 'rtl' ? (
) : (
@@ -280,16 +278,14 @@ AppFrame.propTypes = {
children: PropTypes.node.isRequired,
classes: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
- uiTheme: PropTypes.object.isRequired,
- userLanguage: PropTypes.string.isRequired,
+ options: PropTypes.object.isRequired,
+ reduxTheme: PropTypes.object.isRequired,
};
-const pageContext = fromRenderProps(PageContext.Consumer, ({ userLanguage }) => ({ userLanguage }));
-
export default compose(
connect(state => ({
- uiTheme: state.theme,
+ options: state.options,
+ reduxTheme: state.theme,
})),
- pageContext,
withStyles(styles),
)(AppFrame);
diff --git a/docs/src/modules/components/AppSearch.js b/docs/src/modules/components/AppSearch.js
index 0c841746d06439..7c4f854f815a5e 100644
--- a/docs/src/modules/components/AppSearch.js
+++ b/docs/src/modules/components/AppSearch.js
@@ -88,6 +88,7 @@ const styles = theme => ({
},
'& .algolia-docsearch-suggestion': {
textDecoration: 'none',
+ backgroundColor: theme.palette.background.paper,
},
'& .algolia-docsearch-suggestion--title': theme.typography.h6,
'& .algolia-docsearch-suggestion--text': theme.typography.body2,
@@ -186,6 +187,6 @@ AppSearch.propTypes = {
};
export default compose(
- withStyles(styles),
withWidth(),
+ withStyles(styles),
)(AppSearch);
diff --git a/docs/src/modules/components/AppTableOfContents.js b/docs/src/modules/components/AppTableOfContents.js
index ec6aa732a0873f..ea750bc6b0c54a 100644
--- a/docs/src/modules/components/AppTableOfContents.js
+++ b/docs/src/modules/components/AppTableOfContents.js
@@ -38,7 +38,9 @@ renderer.heading = (text, level) => {
const styles = theme => ({
root: {
top: 70,
- width: 162,
+ // Fix IE 11 position sticky issue.
+ marginTop: 70,
+ width: 167,
flexShrink: 0,
order: 2,
position: 'sticky',
@@ -46,7 +48,7 @@ const styles = theme => ({
height: 'calc(100vh - 70px)',
overflowY: 'auto',
padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 2}px ${theme.spacing.unit *
- 2}px 0`,
+ 2}px 5px`,
display: 'none',
[theme.breakpoints.up('sm')]: {
display: 'block',
diff --git a/docs/src/modules/components/AppWrapper.js b/docs/src/modules/components/AppWrapper.js
index 7e5e0539dce197..323dd041373ef1 100644
--- a/docs/src/modules/components/AppWrapper.js
+++ b/docs/src/modules/components/AppWrapper.js
@@ -3,10 +3,16 @@
import React from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
+import url from 'url';
+import acceptLanguage from 'accept-language';
import { ThemeProvider, StylesProvider } from '@material-ui/styles';
import { lightTheme, darkTheme, setPrismTheme } from '@material-ui/docs/MarkdownElement/prism';
import getPageContext, { updatePageContext } from 'docs/src/modules/styles/getPageContext';
import GoogleAnalytics from 'docs/src/modules/components/GoogleAnalytics';
+import { getCookie } from 'docs/src/modules/utils/helpers';
+import { ACTION_TYPES } from 'docs/src/modules/constants';
+
+acceptLanguage.languages(['en', 'zh']);
// Inject the insertion-point-jss after docssearch
if (process.browser && !global.__INSERTION_POINT__) {
@@ -19,16 +25,16 @@ if (process.browser && !global.__INSERTION_POINT__) {
}
}
-function uiThemeSideEffect(uiTheme) {
- setPrismTheme(uiTheme.paletteType === 'light' ? lightTheme : darkTheme);
- document.body.dir = uiTheme.direction;
+function themeSideEffect(reduxTheme) {
+ setPrismTheme(reduxTheme.paletteType === 'light' ? lightTheme : darkTheme);
+ document.body.dir = reduxTheme.direction;
}
class AppWrapper extends React.Component {
state = {};
componentDidMount() {
- uiThemeSideEffect(this.props.uiTheme);
+ themeSideEffect(this.props.reduxTheme);
// Remove the server-side injected CSS.
const jssStyles = document.querySelector('#jss-server-side');
@@ -43,10 +49,42 @@ class AppWrapper extends React.Component {
) {
navigator.serviceWorker.register('/sw.js');
}
+
+ const { options, reduxTheme } = this.props;
+
+ const paletteType = getCookie('paletteType');
+ const paletteColors = getCookie('paletteColors');
+
+ if (reduxTheme.paletteType !== paletteType || reduxTheme.paletteColors !== paletteColors) {
+ this.props.dispatch({
+ type: ACTION_TYPES.THEME_CHANGE,
+ payload: {
+ paletteType,
+ paletteColors: paletteColors ? JSON.parse(paletteColors) : null,
+ },
+ });
+ }
+
+ const URL = url.parse(document.location.href, true);
+ const userLanguage = acceptLanguage.get(
+ URL.query.lang || getCookie('lang') || navigator.language || 'en',
+ );
+
+ const codeVariant = getCookie('codeVariant');
+
+ if (options.userLanguage !== userLanguage || options.codeVariant !== codeVariant) {
+ this.props.dispatch({
+ type: ACTION_TYPES.OPTIONS_CHANGE,
+ payload: {
+ userLanguage,
+ codeVariant,
+ },
+ });
+ }
}
componentDidUpdate() {
- uiThemeSideEffect(this.props.uiTheme);
+ themeSideEffect(this.props.reduxTheme);
}
static getDerivedStateFromProps(nextProps, prevState) {
@@ -60,13 +98,13 @@ class AppWrapper extends React.Component {
const { prevProps } = prevState;
if (
- nextProps.uiTheme.paletteType !== prevProps.uiTheme.paletteType ||
- nextProps.uiTheme.paletteColors !== prevProps.uiTheme.paletteColors ||
- nextProps.uiTheme.direction !== prevProps.uiTheme.direction
+ nextProps.reduxTheme.paletteType !== prevProps.reduxTheme.paletteType ||
+ nextProps.reduxTheme.paletteColors !== prevProps.reduxTheme.paletteColors ||
+ nextProps.reduxTheme.direction !== prevProps.reduxTheme.direction
) {
return {
prevProps: nextProps,
- pageContext: updatePageContext(nextProps.uiTheme),
+ pageContext: updatePageContext(nextProps.reduxTheme),
};
}
@@ -74,7 +112,7 @@ class AppWrapper extends React.Component {
}
render() {
- const { children } = this.props;
+ const { children, options } = this.props;
const { pageContext } = this.state;
return (
@@ -84,7 +122,11 @@ class AppWrapper extends React.Component {
sheetsManager={pageContext.sheetsManager}
sheetsRegistry={pageContext.sheetsRegistry}
>
- {children}
+
+ {React.cloneElement(children, {
+ lang: options.userLanguage === 'en' ? '' : `-${options.userLanguage}`,
+ })}
+
);
@@ -93,11 +135,14 @@ class AppWrapper extends React.Component {
AppWrapper.propTypes = {
children: PropTypes.node.isRequired,
+ dispatch: PropTypes.func.isRequired,
+ options: PropTypes.object.isRequired,
// eslint-disable-next-line react/no-unused-prop-types
pageContext: PropTypes.object,
- uiTheme: PropTypes.object.isRequired,
+ reduxTheme: PropTypes.object.isRequired,
};
export default connect(state => ({
- uiTheme: state.theme,
+ options: state.options,
+ reduxTheme: state.theme,
}))(AppWrapper);
diff --git a/docs/src/modules/components/Demo.js b/docs/src/modules/components/Demo.js
index fa03648916b445..9c2740a4bbff86 100644
--- a/docs/src/modules/components/Demo.js
+++ b/docs/src/modules/components/Demo.js
@@ -3,6 +3,8 @@ import LZString from 'lz-string';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import copy from 'clipboard-copy';
+import { connect } from 'react-redux';
+import compose from 'recompose/compose';
import { withStyles } from '@material-ui/core/styles';
import IconButton from '@material-ui/core/IconButton';
import Collapse from '@material-ui/core/Collapse';
@@ -13,9 +15,12 @@ import MenuItem from '@material-ui/core/MenuItem';
import MoreVertIcon from '@material-ui/icons/MoreVert';
import Tooltip from '@material-ui/core/Tooltip';
import Github from '@material-ui/docs/svgIcons/GitHub';
+import JSLogo from '@material-ui/docs/svgIcons/JSLogo';
+import HookLogo from '@material-ui/docs/svgIcons/HookLogo';
import MarkdownElement from '@material-ui/docs/MarkdownElement';
import { getDependencies } from 'docs/src/modules/utils/helpers';
import DemoFrame from 'docs/src/modules/components/DemoFrame';
+import { ACTION_TYPES, CODE_VARIANTS } from 'docs/src/modules/constants';
function compress(object) {
return LZString.compressToBase64(JSON.stringify(object))
@@ -32,13 +37,13 @@ function addHiddenInput(form, name, value) {
form.appendChild(input);
}
-function getDemo(props) {
+function getDemo(props, raw) {
return {
title: 'Material demo',
description: props.githubLocation,
- dependencies: getDependencies(props.raw),
+ dependencies: getDependencies(raw, props.demoOptions.react),
files: {
- 'demo.js': props.raw,
+ 'demo.js': raw,
'index.js': `
import React from 'react';
import ReactDOM from 'react-dom';
@@ -103,12 +108,13 @@ const styles = theme => ({
code: {
display: 'none',
padding: 0,
- margin: 0,
+ marginRight: 0,
[theme.breakpoints.up('sm')]: {
display: 'block',
},
'& pre': {
overflow: 'auto',
+ paddingTop: theme.spacing.unit * 5,
margin: '0px !important',
borderRadius: '0px !important',
},
@@ -129,14 +135,8 @@ class Demo extends React.Component {
this.setState({ anchorEl: null });
};
- handleClickCodeOpen = () => {
- this.setState(state => ({
- codeOpen: !state.codeOpen,
- }));
- };
-
handleClickCodeSandbox = () => {
- const demo = getDemo(this.props);
+ const demo = getDemo(this.props, this.getDemoData().raw);
const parameters = compress({
files: {
'package.json': {
@@ -170,14 +170,15 @@ class Demo extends React.Component {
handleClickCopy = async () => {
try {
- await copy(this.props.raw);
+ await copy(this.getDemoData().raw);
} finally {
this.handleCloseMore();
}
};
handleClickStackBlitz = () => {
- const demo = getDemo(this.props);
+ const { codeVariant } = this.state;
+ const demo = getDemo(this.props, codeVariant);
const form = document.createElement('form');
form.method = 'POST';
form.target = '_blank';
@@ -196,16 +197,101 @@ class Demo extends React.Component {
this.handleCloseMore();
};
+ handleCodeLanguageClick = event => {
+ const codeVariant = event.currentTarget.value;
+
+ if (this.props.options.codeVariant !== codeVariant) {
+ document.cookie = `codeVariant=${codeVariant};path=/;max-age=31536000`;
+
+ this.props.dispatch({
+ type: ACTION_TYPES.OPTIONS_CHANGE,
+ payload: {
+ codeVariant,
+ },
+ });
+ }
+
+ this.setState(prevState => {
+ return {
+ /**
+ * if the the same code type is open,
+ * toggle the state, otherwise if it is
+ * another code type always open it. i.e, true
+ */
+ codeOpen: this.props.options.codeVariant === codeVariant ? !prevState.codeOpen : true,
+ };
+ });
+ };
+
+ handleClickCodeOpen = () => {
+ this.setState(state => ({
+ codeOpen: !state.codeOpen,
+ }));
+ };
+
+ getDemoData = () => {
+ const { options, demo } = this.props;
+ return options.codeVariant === CODE_VARIANTS.HOOK && demo.rawHooks
+ ? {
+ codeVariant: CODE_VARIANTS.HOOK,
+ raw: demo.rawHooks,
+ js: demo.jsHooks,
+ }
+ : {
+ codeVariant: CODE_VARIANTS.JS,
+ js: demo.js,
+ raw: demo.raw,
+ };
+ };
+
render() {
- const { classes, demoOptions, githubLocation, index, js: DemoComponent, raw } = this.props;
+ const { classes, demo, demoOptions, githubLocation: githubLocationJS } = this.props;
const { anchorEl, codeOpen } = this.state;
const category = demoOptions.demo;
+ const demoData = this.getDemoData();
+ const DemoComponent = demoData.js;
+ const githubLocation =
+ demoData.codeVariant === CODE_VARIANTS.HOOK
+ ? githubLocationJS.replace(/\.jsx?$/, '.hooks.js')
+ : githubLocationJS;
return (
{demoOptions.hideHeader ? null : (
+ {demo.rawHooks && (
+
+
+
+
+
+ )}
+ {demo.rawHooks && (
+
+
+
+
+
+ )}
+
+
+
+
+
)}
-
-
-
-
-
@@ -309,11 +385,16 @@ class Demo extends React.Component {
Demo.propTypes = {
classes: PropTypes.object.isRequired,
+ demo: PropTypes.object.isRequired,
demoOptions: PropTypes.object.isRequired,
+ dispatch: PropTypes.func.isRequired,
githubLocation: PropTypes.string.isRequired,
- index: PropTypes.number.isRequired,
- js: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
- raw: PropTypes.string.isRequired,
+ options: PropTypes.object.isRequired,
};
-export default withStyles(styles)(Demo);
+export default compose(
+ connect(state => ({
+ options: state.options,
+ })),
+ withStyles(styles),
+)(Demo);
diff --git a/docs/src/modules/components/DemoFrame.js b/docs/src/modules/components/DemoFrame.js
index 79e638cb28899b..7226351c8c2f30 100644
--- a/docs/src/modules/components/DemoFrame.js
+++ b/docs/src/modules/components/DemoFrame.js
@@ -1,13 +1,9 @@
import React from 'react';
import PropTypes from 'prop-types';
import { create } from 'jss';
-import {
- withStyles,
- createGenerateClassName,
- jssPreset,
- MuiThemeProvider,
-} from '@material-ui/core/styles';
+import { withStyles, jssPreset } from '@material-ui/core/styles';
import { StylesProvider } from '@material-ui/styles';
+import NoSsr from '@material-ui/core/NoSsr';
import rtl from 'jss-rtl';
import Frame from 'react-frame-component';
@@ -21,8 +17,6 @@ const styles = theme => ({
},
});
-const generateClassName = createGenerateClassName();
-
class DemoFrame extends React.Component {
state = {
ready: false,
@@ -49,28 +43,27 @@ class DemoFrame extends React.Component {
};
render() {
- const { children, classes, theme } = this.props;
-
- const inIframe = this.state.ready ? (
-
-
- {React.cloneElement(children, {
- container: this.state.container,
- })}
-
-
- ) : null;
+ const { children, classes } = this.props;
+ // NoSsr fixes a strange concurrency issue with iframe and quick React mount/unmount
return (
-
-
- {inIframe}
-
+
+
+
+ {this.state.ready ? (
+
+ {React.cloneElement(children, {
+ container: this.state.container,
+ })}
+
+ ) : null}
+
+
);
}
}
diff --git a/docs/src/modules/components/EditPage.js b/docs/src/modules/components/EditPage.js
index 00deab868bab80..a8f5e906f1c29b 100644
--- a/docs/src/modules/components/EditPage.js
+++ b/docs/src/modules/components/EditPage.js
@@ -1,35 +1,30 @@
import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
-import PageContext from 'docs/src/modules/components/PageContext';
+import { connect } from 'react-redux';
function EditPage(props) {
- const { markdownLocation, sourceCodeRootUrl } = props;
+ const { markdownLocation, options, sourceCodeRootUrl } = props;
- return (
-
- {({ userLanguage }) => {
- if (userLanguage === 'zh') {
- return (
-
- {'将此页面翻译成中文'}
-
- );
- }
+ if (options.userLanguage === 'zh') {
+ return (
+
+ {'将此页面翻译成中文'}
+
+ );
+ }
- return (
-
- {'Edit this page'}
-
- );
- }}
-
+ return (
+
+ {'Edit this page'}
+
);
}
EditPage.propTypes = {
markdownLocation: PropTypes.string.isRequired,
+ options: PropTypes.object.isRequired,
sourceCodeRootUrl: PropTypes.string.isRequired,
};
-export default EditPage;
+export default connect(state => ({ options: state.options }))(EditPage);
diff --git a/docs/src/modules/components/HomeBackers.js b/docs/src/modules/components/HomeBackers.js
index 84bcc16bc1d207..0a8edf96c0c19e 100644
--- a/docs/src/modules/components/HomeBackers.js
+++ b/docs/src/modules/components/HomeBackers.js
@@ -37,15 +37,24 @@ Gold Sponsors are those who have pledged $500/month and more to Material-UI.
via [Patreon](https://www.patreon.com/oliviertassinari)
-
-
-
+
+
+
+
+
+
+
via [OpenCollective](https://opencollective.com/material-ui)
-
- Gold Sponsors
+
+
+
+
+
+
+
### There is more!
diff --git a/docs/src/modules/components/HomeSteps.js b/docs/src/modules/components/HomeSteps.js
index 16e7f27a62055f..fe2a1044332066 100644
--- a/docs/src/modules/components/HomeSteps.js
+++ b/docs/src/modules/components/HomeSteps.js
@@ -7,7 +7,7 @@ import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import Divider from '@material-ui/core/Divider';
import FileDownloadIcon from '@material-ui/docs/svgIcons/FileDownload';
-import BuildIcon from '@material-ui/icons/Build'; // eslint-disable-line import/no-unresolved
+import BuildIcon from '@material-ui/icons/Build';
import WhatshotIcon from '@material-ui/icons/Whatshot';
import MarkdownElement from '@material-ui/docs/MarkdownElement';
import NoSsr from '@material-ui/core/NoSsr';
diff --git a/docs/src/modules/components/MarkdownDocs.js b/docs/src/modules/components/MarkdownDocs.js
index 89e0af6b1fb9af..93c4b38e1a37df 100644
--- a/docs/src/modules/components/MarkdownDocs.js
+++ b/docs/src/modules/components/MarkdownDocs.js
@@ -58,7 +58,7 @@ function MarkdownDocs(props) {
sourceCodeRootUrl={SOURCE_CODE_ROOT_URL}
/>
- {contents.map((content, index) => {
+ {contents.map(content => {
const match = content.match(demoRegexp);
if (match && demos) {
@@ -75,9 +75,7 @@ function MarkdownDocs(props) {
return (
diff --git a/docs/src/modules/components/PageContext.js b/docs/src/modules/components/PageContext.js
index 55434067a7c967..408a76ba983fe6 100644
--- a/docs/src/modules/components/PageContext.js
+++ b/docs/src/modules/components/PageContext.js
@@ -7,7 +7,6 @@ const PageContext = React.createContext({
pathname: '',
},
pages: [],
- userLanguage: '',
});
export default PageContext;
diff --git a/docs/src/modules/constants.js b/docs/src/modules/constants.js
new file mode 100644
index 00000000000000..7dd297cee167a4
--- /dev/null
+++ b/docs/src/modules/constants.js
@@ -0,0 +1,9 @@
+export const CODE_VARIANTS = {
+ JS: 'JS',
+ HOOK: 'HOOK',
+};
+
+export const ACTION_TYPES = {
+ THEME_CHANGE: 'THEME_CHANGE',
+ OPTIONS_CHANGE: 'OPTIONS_CHANGE',
+};
diff --git a/docs/src/modules/redux/actionTypes.js b/docs/src/modules/redux/actionTypes.js
deleted file mode 100644
index a8347364b2428d..00000000000000
--- a/docs/src/modules/redux/actionTypes.js
+++ /dev/null
@@ -1,7 +0,0 @@
-const actionTypes = {
- THEME_CHANGE_DIRECTION: 'THEME_CHANGE_DIRECTION',
- THEME_CHANGE_PALETTE_COLORS: 'THEME_CHANGE_PALETTE_COLORS',
- THEME_CHANGE_PALETTE_TYPE: 'THEME_CHANGE_PALETTE_TYPE',
-};
-
-export default actionTypes;
diff --git a/docs/src/modules/redux/initRedux.js b/docs/src/modules/redux/initRedux.js
index 69d9af474b84d1..de81b3689dd2fa 100644
--- a/docs/src/modules/redux/initRedux.js
+++ b/docs/src/modules/redux/initRedux.js
@@ -2,6 +2,7 @@
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import themeReducer from 'docs/src/modules/redux/themeReducer';
+import optionsReducer from 'docs/src/modules/redux/optionsReducer';
// Get the Redux DevTools extension and fallback to a no-op function
let devtools = x => x;
@@ -32,6 +33,7 @@ function create(initialState) {
return createStore(
combineReducers({
theme: themeReducer,
+ options: optionsReducer,
}),
initialState, // Hydrate the store with server-side data
compose(
diff --git a/docs/src/modules/redux/optionsReducer.js b/docs/src/modules/redux/optionsReducer.js
new file mode 100644
index 00000000000000..0f5a4883ebeba8
--- /dev/null
+++ b/docs/src/modules/redux/optionsReducer.js
@@ -0,0 +1,25 @@
+import { ACTION_TYPES, CODE_VARIANTS } from 'docs/src/modules/constants';
+
+const mapping = {
+ [ACTION_TYPES.OPTIONS_CHANGE]: (state, action) => ({
+ codeVariant: action.payload.codeVariant || state.codeVariant,
+ userLanguage: action.payload.userLanguage || state.userLanguage,
+ }),
+};
+
+const initialState = {
+ codeVariant: CODE_VARIANTS.JS,
+ userLanguage: 'en',
+};
+
+function optionsReducer(state = initialState, action) {
+ let newState = state;
+
+ if (mapping[action.type]) {
+ newState = mapping[action.type](state, action);
+ }
+
+ return newState;
+}
+
+export default optionsReducer;
diff --git a/docs/src/modules/redux/themeReducer.js b/docs/src/modules/redux/themeReducer.js
index 4dbd088c05149f..680940c1f9fc4a 100644
--- a/docs/src/modules/redux/themeReducer.js
+++ b/docs/src/modules/redux/themeReducer.js
@@ -1,18 +1,11 @@
-import actionTypes from 'docs/src/modules/redux/actionTypes';
+import { ACTION_TYPES } from 'docs/src/modules/constants';
import themeInitialState from 'docs/src/modules/styles/themeInitialState';
const mapping = {
- [actionTypes.THEME_CHANGE_PALETTE_TYPE]: (state, action) => ({
- ...state,
- paletteType: action.payload.paletteType,
- }),
- [actionTypes.THEME_CHANGE_DIRECTION]: (state, action) => ({
- ...state,
- direction: action.payload.direction,
- }),
- [actionTypes.THEME_CHANGE_PALETTE_COLORS]: (state, action) => ({
- ...state,
- paletteColors: action.payload.paletteColors,
+ [ACTION_TYPES.THEME_CHANGE]: (state, action) => ({
+ paletteType: action.payload.paletteType || state.paletteType,
+ direction: action.payload.direction || state.direction,
+ paletteColors: action.payload.paletteColors || state.paletteColors,
}),
};
diff --git a/docs/src/modules/styles/getPageContext.js b/docs/src/modules/styles/getPageContext.js
index 5fa71820d42a04..b5fd8bedfb5b91 100644
--- a/docs/src/modules/styles/getPageContext.js
+++ b/docs/src/modules/styles/getPageContext.js
@@ -2,7 +2,8 @@
import { create, SheetsRegistry } from 'jss';
import rtl from 'jss-rtl';
-import { createMuiTheme, createGenerateClassName, jssPreset } from '@material-ui/core/styles';
+import { createGenerateClassName, jssPreset } from '@material-ui/styles';
+import { createMuiTheme } from '@material-ui/core/styles';
import themeInitialState from './themeInitialState';
function getTheme(uiTheme) {
diff --git a/docs/src/modules/utils/generateMarkdown.js b/docs/src/modules/utils/generateMarkdown.js
index 1f888a82bcddd5..e7a4ccc4f68091 100644
--- a/docs/src/modules/utils/generateMarkdown.js
+++ b/docs/src/modules/utils/generateMarkdown.js
@@ -20,7 +20,7 @@ function generateHeader(reactAPI) {
}
function getDeprecatedInfo(type) {
- const marker = 'deprecated(PropTypes.';
+ const marker = 'deprecatedPropType(PropTypes.';
const indexStart = type.raw.indexOf(marker);
if (indexStart !== -1) {
@@ -149,6 +149,10 @@ function generatePropType(type) {
return generatePropType(chained);
}
+ if (type.raw === 'componentProp') {
+ return 'Component';
+ }
+
return type.raw;
}
diff --git a/docs/src/modules/utils/helpers.js b/docs/src/modules/utils/helpers.js
index 4fa75b6529417f..9c2b4ac772d582 100644
--- a/docs/src/modules/utils/helpers.js
+++ b/docs/src/modules/utils/helpers.js
@@ -28,10 +28,10 @@ export function pageToTitle(page) {
return titleize(name);
}
-export function getDependencies(raw) {
+export function getDependencies(raw, reactVersion = 'latest') {
const deps = {
- 'react-dom': 'latest',
- react: 'latest',
+ 'react-dom': reactVersion,
+ react: reactVersion,
};
const re = /^import\s.*\sfrom\s+'([^']+)'/gm;
let m;
diff --git a/docs/src/modules/utils/helpers.test.js b/docs/src/modules/utils/helpers.test.js
index 4b05f5c11ffa4e..82903473074133 100644
--- a/docs/src/modules/utils/helpers.test.js
+++ b/docs/src/modules/utils/helpers.test.js
@@ -33,7 +33,7 @@ import { withStyles } from '@material-ui/core/styles';
const suggestions = [`;
describe('docs getDependencies helpers', () => {
- it('generate the right npm dependencies', () => {
+ it('generates the right npm dependencies', () => {
assert.deepEqual(getDependencies(s1), {
'@foo-bar/bip': 'latest',
'@material-ui/core': 'latest',
@@ -50,5 +50,12 @@ describe('docs getDependencies helpers', () => {
'react-dom': 'latest',
react: 'latest',
});
+ assert.deepEqual(getDependencies(s1, 'next'), {
+ '@foo-bar/bip': 'latest',
+ '@material-ui/core': 'latest',
+ 'prop-types': 'latest',
+ 'react-dom': 'next',
+ react: 'next',
+ });
});
});
diff --git a/docs/src/pages/css-in-js/advanced/advanced.md b/docs/src/pages/css-in-js/advanced/advanced.md
index 92ba74e65a422b..f7b8c83b4acb6f 100644
--- a/docs/src/pages/css-in-js/advanced/advanced.md
+++ b/docs/src/pages/css-in-js/advanced/advanced.md
@@ -6,7 +6,7 @@
Add a `ThemeProvider` to the top level of your app to access the theme down the React's component tree. Then, you can access the theme object in the style functions.
-{{"demo": "pages/css-in-js/advanced/Theming.js"}}
+{{"demo": "pages/css-in-js/advanced/Theming.js", "react": "next"}}
## Accessing the theme in a component
@@ -14,18 +14,39 @@ You might need to access the theme variables inside your React components.
### `useTheme` hook
-{{"demo": "pages/css-in-js/advanced/UseTheme.js"}}
+{{"demo": "pages/css-in-js/advanced/UseTheme.js", "react": "next"}}
### `withTheme` HOC
-{{"demo": "pages/css-in-js/advanced/WithTheme.js"}}
+{{"demo": "pages/css-in-js/advanced/WithTheme.js", "react": "next"}}
## Theme nesting
You can nest multiple theme providers.
This can be really useful when dealing with different area of your application that have distinct appearance from each other.
-{{"demo": "pages/css-in-js/advanced/ThemeNesting.js"}}
+```jsx
+
+
+
+
+
+
+```
+
+{{"demo": "pages/css-in-js/advanced/ThemeNesting.js", "react": "next"}}
+
+The inner theme will **override** the outer theme.
+You can extend the outer theme by providing a function:
+
+```jsx
+
+
+ ({ darkMode: true, ...outerTheme })}>
+
+
+
+```
## JSS plugins
@@ -81,7 +102,7 @@ const useStyles = makeStyles({
});
```
-{{"demo": "pages/css-in-js/advanced/StringTemplates.js"}}
+{{"demo": "pages/css-in-js/advanced/StringTemplates.js", "react": "next"}}
## CSS injection order
diff --git a/docs/src/pages/css-in-js/basics/basics.md b/docs/src/pages/css-in-js/basics/basics.md
index fc817a3a7018c8..d14d31169c52c3 100644
--- a/docs/src/pages/css-in-js/basics/basics.md
+++ b/docs/src/pages/css-in-js/basics/basics.md
@@ -46,8 +46,11 @@ import { install } from '@material-ui/styles';
install();
```
-We will make `@material-ui/styles` the default style implementation for the core components in Material-UI v4.
-This installation step is temporary.
+It is **recommended** to place the above code in a separate file (e.g. `bootstrap.js`) and to import it in your application's entry point (e.g. `index.js`).
+This ensures that the installation is executed before anything else, because ECMAScript imports are hoisted to the top of the module. If the installation step is not performed correctly the resulting build could have conflicting class names.
+
+We will make `@material-ui/styles` the default style implementation for the core components in Material-UI v4. This installation step is **temporary**.
+Behind the scenes, the `install()` function switches the styling engine the core components use.
## Getting started
@@ -79,7 +82,7 @@ export default function Hook() {
}
```
-{{"demo": "pages/css-in-js/basics/Hook.js"}}
+{{"demo": "pages/css-in-js/basics/Hook.js", "react": "next"}}
### Styled components API
@@ -103,7 +106,7 @@ export default function StyledComponents() {
}
```
-{{"demo": "pages/css-in-js/basics/StyledComponents.js"}}
+{{"demo": "pages/css-in-js/basics/StyledComponents.js", "react": "next"}}
### Higher-order component API
@@ -137,7 +140,7 @@ HigherOrderComponent.propTypes = {
export default withStyles(styles)(HigherOrderComponent);
```
-{{"demo": "pages/css-in-js/basics/HigherOrderComponent.js"}}
+{{"demo": "pages/css-in-js/basics/HigherOrderComponent.js", "react": "next"}}
### Render props API
@@ -163,7 +166,7 @@ export default function RenderProps() {
}
```
-{{"demo": "pages/css-in-js/basics/RenderProps.js"}}
+{{"demo": "pages/css-in-js/basics/RenderProps.js", "react": "next"}}
## Adapting based on props
@@ -172,16 +175,16 @@ This button component has a color property that changes its color:
### Adapting hook API
-{{"demo": "pages/css-in-js/basics/AdaptingHook.js"}}
+{{"demo": "pages/css-in-js/basics/AdaptingHook.js", "react":"next"}}
### Adapting styled components API
-{{"demo": "pages/css-in-js/basics/AdaptingStyledComponents.js"}}
+{{"demo": "pages/css-in-js/basics/AdaptingStyledComponents.js", "react": "next"}}
### Adapting higher-order component API
-{{"demo": "pages/css-in-js/basics/AdaptingHOC.js"}}
+{{"demo": "pages/css-in-js/basics/AdaptingHOC.js", "react": "next"}}
### Adapting render props API
-{{"demo": "pages/css-in-js/basics/AdaptingRenderProps.js"}}
+{{"demo": "pages/css-in-js/basics/AdaptingRenderProps.js", "react": "next"}}
diff --git a/docs/src/pages/customization/default-theme/DefaultTheme.js b/docs/src/pages/customization/default-theme/DefaultTheme.js
index 4aff1c71d7945f..c113d2259ad3be 100644
--- a/docs/src/pages/customization/default-theme/DefaultTheme.js
+++ b/docs/src/pages/customization/default-theme/DefaultTheme.js
@@ -34,7 +34,6 @@ class ThemeDefault extends React.Component {
return;
}
- // eslint-disable-next-line react/no-did-mount-set-state
this.setState({
expandPaths: expandPath.split('.').reduce((acc, path) => {
const last = acc.length > 0 ? `${acc[acc.length - 1]}.` : '';
diff --git a/docs/src/pages/customization/overrides/overrides-zh.md b/docs/src/pages/customization/overrides/overrides-zh.md
index 2a1b607a193456..7a8e03588486c8 100644
--- a/docs/src/pages/customization/overrides/overrides-zh.md
+++ b/docs/src/pages/customization/overrides/overrides-zh.md
@@ -22,7 +22,7 @@ In this example, we are using the [`withStyles()`](/customization/css-in-js/#wit
### Overriding with classes
-When the `className` property isn't enough, and you need to access deeper elements, you can take advantage of the `classes` property to customize all the CSS injected by Material-UI for a given component. The list of classes for each component is documented in the **Component API** section. For instance, you can have a look at the [Button CSS API](/api/button/#css-api). Alternatively, you can always look at the [implementation details](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Button/Button.js).
+When the `className` property isn't enough, and you need to access deeper elements, you can take advantage of the `classes` property to customize all the CSS injected by Material-UI for a given component. The list of classes for each component is documented in the **Component API** section. For instance, you can have a look at the [Button CSS API](/api/button/#css). Alternatively, you can always look at the [implementation details](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Button/Button.js).
This example also uses `withStyles()` (see above), but here, `ClassesNesting` is using `Button`'s `classes` prop to provide an object that maps the **names of classes to override** (keys) to the **CSS class names to apply** (values). The component's existing classes will continue to be injected, so it is only necessary to provide the specific styles you wish to add or override.
@@ -32,7 +32,7 @@ Notice that in addition to the button styling, the button label's capitalization
#### Shorthand
-The above code example can be condensed by using **the same CSS API** as the child component. In this example, the `withStyles()` higher-order component is injecting a `classes` property that is used by the [`Button` component](/api/button/#css-api).
+The above code example can be condensed by using **the same CSS API** as the child component. In this example, the `withStyles()` higher-order component is injecting a `classes` property that is used by the [`Button` component](/api/button/#css).
```jsx
const StyledButton = withStyles({
@@ -172,4 +172,4 @@ When the configuration variables aren't powerful enough, you can take advantage
### Global CSS override
-You can also customize all instances of a component with CSS. We expose a `dangerouslyUseGlobalCSS` option to do so. Learn more about it in the [CSS in JS section](/customization/css-in-js/#global-css) of the documentation. It's very similar to how you would customize Bootstrap.
\ No newline at end of file
+You can also customize all instances of a component with CSS. We expose a `dangerouslyUseGlobalCSS` option to do so. Learn more about it in the [CSS in JS section](/customization/css-in-js/#global-css) of the documentation. It's very similar to how you would customize Bootstrap.
diff --git a/docs/src/pages/customization/overrides/overrides.md b/docs/src/pages/customization/overrides/overrides.md
index b4d42174af792b..3f656ef03b0e6b 100644
--- a/docs/src/pages/customization/overrides/overrides.md
+++ b/docs/src/pages/customization/overrides/overrides.md
@@ -2,11 +2,11 @@
As components can be used in different contexts, Material-UI supports different types of customization requirements going from the most specific to the most generic.
-1. [Specific variation for a one-time situation](#1--specific-variation-for-a-one-time-situation)
-1. [Dynamic variation for a one-time situation](#2--dynamic-variation-for-a-one-time-situation)
-1. [Specific variation of a component](#3--specific-variation-of-a-component) re-used in different contexts
-1. [Material Design variations](#4--material-design-variations) such as with the button component
-1. [Global theme variation](#5--global-theme-variation)
+1. [Specific variation for a one-time situation](#1-specific-variation-for-a-one-time-situation)
+1. [Dynamic variation for a one-time situation](#2-dynamic-variation-for-a-one-time-situation)
+1. [Specific variation of a component](#3-specific-variation-of-a-component) re-used in different contexts
+1. [Material Design variations](#4-material-design-variations) such as with the button component
+1. [Global theme variation](#5-global-theme-variation)
## 1. Specific variation for a one-time situation
@@ -31,7 +31,7 @@ of the `
` to ensure the components always render correctly.
When the `className` property isn't enough, and you need to access deeper elements, you can take advantage of the `classes` property to customize all the CSS injected by Material-UI for a given component.
The list of classes for each
component is documented in the **Component API** section.
-For instance, you can have a look at the [Button CSS API](/api/button/#css-api).
+For instance, you can have a look at the [Button CSS API](/api/button/#css).
Alternatively, you can always look at the [implementation details](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Button/Button.js).
This example also uses `withStyles()` (see above), but here, `ClassesNesting` is using `Button`'s `classes` prop to
@@ -46,7 +46,7 @@ Notice that in addition to the button styling, the button label's capitalization
#### Shorthand
The above code example can be condensed by using **the same CSS API** as the child component.
-In this example, the `withStyles()` higher-order component is injecting a `classes` property that is used by the [`Button` component](/api/button/#css-api).
+In this example, the `withStyles()` higher-order component is injecting a `classes` property that is used by the [`Button` component](/api/button/#css).
```jsx
const StyledButton = withStyles({
diff --git a/docs/src/pages/customization/themes/CustomStyles.js b/docs/src/pages/customization/themes/CustomStyles.js
index aac9cd8bd59b3b..7b343f579fadb9 100644
--- a/docs/src/pages/customization/themes/CustomStyles.js
+++ b/docs/src/pages/customization/themes/CustomStyles.js
@@ -35,6 +35,7 @@ const theme = createMuiTheme({
// My business variables
danger: orange[500],
},
+ typography: { useNextVariants: true },
});
function CustomStyles() {
diff --git a/docs/src/pages/customization/themes/DarkTheme.js b/docs/src/pages/customization/themes/DarkTheme.js
index 26973d4abbce2c..fc4bb2e20780c0 100644
--- a/docs/src/pages/customization/themes/DarkTheme.js
+++ b/docs/src/pages/customization/themes/DarkTheme.js
@@ -6,9 +6,7 @@ const theme = createMuiTheme({
palette: {
type: 'dark', // Switching the dark mode on is a single property value change.
},
- typography: {
- useNextVariants: true,
- },
+ typography: { useNextVariants: true },
});
function DarkTheme() {
diff --git a/docs/src/pages/customization/themes/Nested.js b/docs/src/pages/customization/themes/Nested.js
deleted file mode 100644
index 7fb454ba514932..00000000000000
--- a/docs/src/pages/customization/themes/Nested.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import React from 'react';
-import PropTypes from 'prop-types';
-import Checkbox from '@material-ui/core/Checkbox';
-import { createMuiTheme, MuiThemeProvider, withStyles } from '@material-ui/core/styles';
-import orange from '@material-ui/core/colors/orange';
-import green from '@material-ui/core/colors/green';
-import pink from '@material-ui/core/colors/pink';
-
-const styles = theme => ({
- root: {
- color: theme.status.color,
- '&$checked': {
- color: theme.status.color,
- },
- },
- checked: {},
-});
-
-let NestedCheckbox = props => (
-
-);
-
-NestedCheckbox.propTypes = {
- classes: PropTypes.object.isRequired,
-};
-
-NestedCheckbox = withStyles(styles)(NestedCheckbox);
-
-const theme1 = createMuiTheme({
- status: {
- color: orange[500],
- },
-});
-
-const theme2 = createMuiTheme({
- status: {
- color: green[500],
- },
-});
-
-const theme3 = outerTheme => ({
- ...outerTheme,
- status: {
- color: pink[500],
- },
-});
-
-function Nested() {
- return (
-
-
-
-
-
-
-
-
-
- );
-}
-
-export default Nested;
diff --git a/docs/src/pages/customization/themes/OverridesCss.js b/docs/src/pages/customization/themes/OverridesCss.js
index baa83230c1c7a5..185d48f0e9f904 100644
--- a/docs/src/pages/customization/themes/OverridesCss.js
+++ b/docs/src/pages/customization/themes/OverridesCss.js
@@ -7,7 +7,7 @@ const theme = createMuiTheme({
// Name of the component ⚛️ / style sheet
MuiButton: {
// Name of the rule
- root: {
+ text: {
// Some CSS
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
borderRadius: 3,
@@ -19,6 +19,7 @@ const theme = createMuiTheme({
},
},
},
+ typography: { useNextVariants: true },
});
function OverridesCss() {
diff --git a/docs/src/pages/customization/themes/OverridesProperties.js b/docs/src/pages/customization/themes/OverridesProperties.js
index b6fe6a0077cbab..0fda441e3c29e2 100644
--- a/docs/src/pages/customization/themes/OverridesProperties.js
+++ b/docs/src/pages/customization/themes/OverridesProperties.js
@@ -10,6 +10,7 @@ const theme = createMuiTheme({
disableRipple: true, // No more ripple, on the whole application 💣!
},
},
+ typography: { useNextVariants: true },
});
function OverridesProperties() {
diff --git a/docs/src/pages/customization/themes/Palette.js b/docs/src/pages/customization/themes/Palette.js
index 548d6d85bd7d83..cf2cfbf8ca8d5c 100644
--- a/docs/src/pages/customization/themes/Palette.js
+++ b/docs/src/pages/customization/themes/Palette.js
@@ -8,6 +8,7 @@ const theme = createMuiTheme({
primary: { main: purple[500] }, // Purple and green play nicely together.
secondary: { main: '#11cb5f' }, // This is just green.A700 as hex.
},
+ typography: { useNextVariants: true },
});
function Palette() {
diff --git a/docs/src/pages/customization/themes/ThemeNesting.js b/docs/src/pages/customization/themes/ThemeNesting.js
new file mode 100644
index 00000000000000..76017781d976fd
--- /dev/null
+++ b/docs/src/pages/customization/themes/ThemeNesting.js
@@ -0,0 +1,36 @@
+import React from 'react';
+import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles';
+import Checkbox from '@material-ui/core/Checkbox';
+import orange from '@material-ui/core/colors/orange';
+import green from '@material-ui/core/colors/green';
+
+const outerTheme = createMuiTheme({
+ palette: {
+ secondary: {
+ main: orange[500],
+ },
+ },
+ typography: { useNextVariants: true },
+});
+
+const innerTheme = createMuiTheme({
+ palette: {
+ secondary: {
+ main: green[500],
+ },
+ },
+ typography: { useNextVariants: true },
+});
+
+function ThemeNesting() {
+ return (
+
+
+
+
+
+
+ );
+}
+
+export default ThemeNesting;
diff --git a/docs/src/pages/customization/themes/ThemeNestingExtend.js b/docs/src/pages/customization/themes/ThemeNestingExtend.js
new file mode 100644
index 00000000000000..fea1d4cf61af99
--- /dev/null
+++ b/docs/src/pages/customization/themes/ThemeNestingExtend.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import { createMuiTheme, MuiThemeProvider } from '@material-ui/core/styles';
+import Checkbox from '@material-ui/core/Checkbox';
+import orange from '@material-ui/core/colors/orange';
+import green from '@material-ui/core/colors/green';
+
+const outerTheme = createMuiTheme({
+ palette: {
+ secondary: {
+ main: orange[500],
+ },
+ },
+ typography: { useNextVariants: true },
+});
+
+function ThemeNestingExtend() {
+ return (
+
+
+
+ createMuiTheme({
+ ...theme,
+ palette: {
+ ...theme.palette,
+ primary: {
+ main: green[500],
+ },
+ },
+ })
+ }
+ >
+
+
+
+
+ );
+}
+
+export default ThemeNestingExtend;
diff --git a/docs/src/pages/customization/themes/themes-zh.md b/docs/src/pages/customization/themes/themes-zh.md
index 9fe90f14b0732d..461a80cdf2362f 100644
--- a/docs/src/pages/customization/themes/themes-zh.md
+++ b/docs/src/pages/customization/themes/themes-zh.md
@@ -19,7 +19,7 @@ If you wish to customize the theme, you need to use the `MuiThemeProvider` compo
Changing the theme configuration variables is the most effective way to match Material-UI to your needs. The following sections cover the most important theme variables:
- [Palette](#palette)
-- [Type (light / dark theme)](#type-light-dark-theme-)
+- [Type (light / dark theme)](#type-light-dark-theme)
- [Typography](#typography)
- [Other variables](#other-variables)
- [Custom variables](#custom-variables)
@@ -288,7 +288,7 @@ const theme = createMuiTheme({
{{"demo": "pages/customization/themes/OverridesCss.js"}}
-The list of these customization points for each component is documented under the **Component API** section. For instance, you can have a look at the [Button](/api/button/#css-api). Alternatively, you can always have a look at the [implementation](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Button/Button.js).
+The list of these customization points for each component is documented under the **Component API** section. For instance, you can have a look at the [Button](/api/button/#css). Alternatively, you can always have a look at the [implementation](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Button/Button.js).
### Properties
@@ -300,7 +300,7 @@ const theme = createMuiTheme({
// Name of the component ⚛️
MuiButtonBase: {
// The properties to apply
- disableRipple: true, // No more ripple, on the whole application
+ disableRipple: true, // No more ripple, on the whole application
},
},
});
@@ -408,4 +408,4 @@ function MyComponent(props) {
}
export default withTheme()(MyComponent);
-```
\ No newline at end of file
+```
diff --git a/docs/src/pages/customization/themes/themes.md b/docs/src/pages/customization/themes/themes.md
index 09541064871903..7e6eb0834e92f5 100644
--- a/docs/src/pages/customization/themes/themes.md
+++ b/docs/src/pages/customization/themes/themes.md
@@ -23,7 +23,7 @@ Changing the theme configuration variables is the most effective way to match Ma
The following sections cover the most important theme variables:
- [Palette](#palette)
-- [Type (light / dark theme)](#type-light-dark-theme-)
+- [Type (light / dark theme)](#type-light-dark-theme)
- [Typography](#typography)
- [Other variables](#other-variables)
- [Custom variables](#custom-variables)
@@ -314,7 +314,7 @@ That's a really powerful feature.
const theme = createMuiTheme({
overrides: {
MuiButton: { // Name of the component ⚛️ / style sheet
- root: { // Name of the rule
+ text: { // Name of the rule
color: 'white', // Some CSS
},
},
@@ -325,7 +325,7 @@ const theme = createMuiTheme({
{{"demo": "pages/customization/themes/OverridesCss.js"}}
The list of these customization points for each component is documented under the **Component API** section.
-For instance, you can have a look at the [Button](/api/button/#css-api).
+For instance, you can have a look at the [Button](/api/button/#css).
Alternatively, you can always have a look at the [implementation](https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Button/Button.js).
### Properties
@@ -356,10 +356,15 @@ Let's say you want to display the value of the primary color, you can use the `w
## Nesting the theme
-The theming solution is very flexible, as you can nest multiple theme providers.
+The theming solution is very flexible, as [you can nest](/css-in-js/advanced/#theme-nesting) multiple theme providers.
This can be really useful when dealing with different area of your application that have distinct appearance from each other.
-{{"demo": "pages/customization/themes/Nested.js"}}
+{{"demo": "pages/customization/themes/ThemeNesting.js"}}
+
+The inner theme will **override** the outer theme.
+You can extend the outer theme by providing a function:
+
+{{"demo": "pages/customization/themes/ThemeNestingExtend.js"}}
#### A note on performance
diff --git a/docs/src/pages/demos/app-bar/app-bar.md b/docs/src/pages/demos/app-bar/app-bar.md
index 0e0e1e19ac81c7..b7da82d67e4599 100644
--- a/docs/src/pages/demos/app-bar/app-bar.md
+++ b/docs/src/pages/demos/app-bar/app-bar.md
@@ -11,13 +11,19 @@ The [top App Bar](https://material.io/design/components/app-bars-top.html) provi
It can transform into a contextual action bar or used as a navbar.
+## App Bar with buttons
+
+{{"demo": "pages/demos/app-bar/ButtonAppBar.js"}}
+
## Simple App Bar
{{"demo": "pages/demos/app-bar/SimpleAppBar.js"}}
-## App Bar with buttons
+## App Bar with a primary search field
-{{"demo": "pages/demos/app-bar/ButtonAppBar.js"}}
+A primary searchbar.
+
+{{"demo": "pages/demos/app-bar/PrimarySearchAppBar.js"}}
## App Bar with menu
@@ -29,12 +35,6 @@ A side searchbar.
{{"demo": "pages/demos/app-bar/SearchAppBar.js"}}
-## App Bar with a primary search field
-
-A primary searchbar.
-
-{{"demo": "pages/demos/app-bar/PrimarySearchAppBar.js"}}
-
## Dense (desktop only)
{{"demo": "pages/demos/app-bar/DenseAppBar.js"}}
diff --git a/docs/src/pages/demos/autocomplete/autocomplete.md b/docs/src/pages/demos/autocomplete/autocomplete.md
index 436cf6f2395bc7..1ab4e64f4db94d 100644
--- a/docs/src/pages/demos/autocomplete/autocomplete.md
+++ b/docs/src/pages/demos/autocomplete/autocomplete.md
@@ -19,6 +19,15 @@ In the following example, we demonstrate how to use [downshift](https://github.c
{{"demo": "pages/demos/autocomplete/IntegrationDownshift.js"}}
+## react-select
+
+
+
+
+In the following example, we demonstrate how to use [react-select](https://github.com/JedWatson/react-select).
+
+{{"demo": "pages/demos/autocomplete/IntegrationReactSelect.js"}}
+
## react-autosuggest

@@ -28,12 +37,3 @@ In the following example, we demonstrate how to use [react-autosuggest](https://
It's also using [autosuggest-highlight](https://www.npmjs.com/package/autosuggest-highlight) for the highlighting logic.
{{"demo": "pages/demos/autocomplete/IntegrationAutosuggest.js"}}
-
-## react-select
-
-
-
-
-In the following example, we demonstrate how to use [react-select](https://github.com/JedWatson/react-select).
-
-{{"demo": "pages/demos/autocomplete/IntegrationReactSelect.js"}}
diff --git a/docs/src/pages/demos/avatars/avatars.md b/docs/src/pages/demos/avatars/avatars.md
index 14f93b9edffab2..25ae0b41343e02 100644
--- a/docs/src/pages/demos/avatars/avatars.md
+++ b/docs/src/pages/demos/avatars/avatars.md
@@ -13,14 +13,14 @@ Image avatars can be created by passing standard `img` props `src` or `srcSet` i
{{"demo": "pages/demos/avatars/ImageAvatars.js"}}
-## Icon avatars
-
-Icon avatars are created by passing an icon as `children`.
-
-{{"demo": "pages/demos/avatars/IconAvatars.js"}}
-
## Letter avatars
Avatars containing simple characters can be created by passing your string as `children`.
{{"demo": "pages/demos/avatars/LetterAvatars.js"}}
+
+## Icon avatars
+
+Icon avatars are created by passing an icon as `children`.
+
+{{"demo": "pages/demos/avatars/IconAvatars.js"}}
diff --git a/docs/src/pages/demos/badges/badges.md b/docs/src/pages/demos/badges/badges.md
index 88999af72a45c3..48a3e77b0a0ee5 100644
--- a/docs/src/pages/demos/badges/badges.md
+++ b/docs/src/pages/demos/badges/badges.md
@@ -21,4 +21,10 @@ The visibility of badges can be controlled using the `invisible` property.
## Customized Badge
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in,
+here is one example of how you can change the badge position.
+
+⚠️ While the material design specification encourages theming, this example is off the beaten path.
+
{{"demo": "pages/demos/badges/CustomizedBadge.js"}}
diff --git a/docs/src/pages/demos/bottom-navigation/SimpleBottomNavigation.hooks.js b/docs/src/pages/demos/bottom-navigation/SimpleBottomNavigation.hooks.js
new file mode 100644
index 00000000000000..7b8376c89ab57f
--- /dev/null
+++ b/docs/src/pages/demos/bottom-navigation/SimpleBottomNavigation.hooks.js
@@ -0,0 +1,35 @@
+import React from 'react';
+import { makeStyles } from '@material-ui/styles';
+import BottomNavigation from '@material-ui/core/BottomNavigation';
+import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';
+import RestoreIcon from '@material-ui/icons/Restore';
+import FavoriteIcon from '@material-ui/icons/Favorite';
+import LocationOnIcon from '@material-ui/icons/LocationOn';
+
+const useStyles = makeStyles({
+ root: {
+ width: 500,
+ },
+});
+
+function SimpleBottomNavigation() {
+ const classes = useStyles();
+ const [value, setValue] = React.useState(0);
+
+ return (
+ {
+ setValue(newValue);
+ }}
+ showLabels
+ className={classes.root}
+ >
+ } />
+ } />
+ } />
+
+ );
+}
+
+export default SimpleBottomNavigation;
diff --git a/docs/src/pages/demos/buttons/IconLabelButtons.js b/docs/src/pages/demos/buttons/IconLabelButtons.js
index 7fcab6653006fa..90614fd4a7f12c 100644
--- a/docs/src/pages/demos/buttons/IconLabelButtons.js
+++ b/docs/src/pages/demos/buttons/IconLabelButtons.js
@@ -5,7 +5,7 @@ import Button from '@material-ui/core/Button';
import { withStyles } from '@material-ui/core/styles';
import DeleteIcon from '@material-ui/icons/Delete';
import CloudUploadIcon from '@material-ui/icons/CloudUpload';
-import KeyboardVoiceICon from '@material-ui/icons/KeyboardVoice';
+import KeyboardVoiceIcon from '@material-ui/icons/KeyboardVoice';
import Icon from '@material-ui/core/Icon';
import SaveIcon from '@material-ui/icons/Save';
@@ -42,7 +42,7 @@ function IconLabelButtons(props) {
-
+
Talk
diff --git a/docs/src/pages/demos/buttons/buttons.md b/docs/src/pages/demos/buttons/buttons.md
index d806b3c4202f04..3deefd45ad8c65 100644
--- a/docs/src/pages/demos/buttons/buttons.md
+++ b/docs/src/pages/demos/buttons/buttons.md
@@ -15,6 +15,16 @@ components: Button, Fab, IconButton, ButtonBase, Zoom
- Cards
- Toolbars
+## Contained Buttons
+
+[Contained buttons](https://material.io/design/components/buttons.html#contained-button)
+are high-emphasis, distinguished by their use of elevation and fill.
+They contain actions that are primary to your app.
+
+The last example of this demo show how to use an upload button.
+
+{{"demo": "pages/demos/buttons/ContainedButtons.js"}}
+
## Text Buttons
[Text buttons](https://material.io/design/components/buttons.html#text-button)
@@ -40,16 +50,6 @@ or a higher emphasis alternative to text buttons.
{{"demo": "pages/demos/buttons/OutlinedButtons.js"}}
-## Contained Buttons
-
-[Contained buttons](https://material.io/design/components/buttons.html#contained-button)
-are high-emphasis, distinguished by their use of elevation and fill.
-They contain actions that are primary to your app.
-
-The last example of this demo show how to use an upload button.
-
-{{"demo": "pages/demos/buttons/ContainedButtons.js"}}
-
## Floating Action Buttons
A [floating action button](https://material.io/design/components/buttons-floating-action-button.html)
@@ -74,27 +74,27 @@ animation to finish before the new one enters.
{{"demo": "pages/demos/buttons/FloatingActionButtonZoom.js"}}
-## Icon Buttons
-
-Icon buttons are commonly found in app bars and toolbars.
-
-Icons are also appropriate for toggle buttons that allow a single choice to be selected or
-deselected, such as adding or removing a star to an item.
-
-{{"demo": "pages/demos/buttons/IconButtons.js"}}
-
## Sizes
Fancy larger or smaller buttons? Use the `size` property.
{{"demo": "pages/demos/buttons/ButtonSizes.js"}}
-### Buttons with icons and label
+## Buttons with icons and label
Sometimes you might want to have icons for certain button to enhance the UX of the application as we recognize logos more easily than plain text. For example, if you have a delete button you can label it with a dustbin icon.
{{"demo": "pages/demos/buttons/IconLabelButtons.js"}}
+## Icon Buttons
+
+Icon buttons are commonly found in app bars and toolbars.
+
+Icons are also appropriate for toggle buttons that allow a single choice to be selected or
+deselected, such as adding or removing a star to an item.
+
+{{"demo": "pages/demos/buttons/IconButtons.js"}}
+
## Customized Buttons
If you have been reading the [overrides documentation page](/customization/overrides/)
@@ -102,6 +102,8 @@ but you are not confident jumping in,
here are examples of how you can change the main color of a Button using classes,
and using a theme; and of a Bootstrap style Button.
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
{{"demo": "pages/demos/buttons/CustomizedButtons.js"}}
## Complex Buttons
diff --git a/docs/src/pages/demos/cards/cards.md b/docs/src/pages/demos/cards/cards.md
index 5126d4d2475f45..1f89ffffeb08c9 100644
--- a/docs/src/pages/demos/cards/cards.md
+++ b/docs/src/pages/demos/cards/cards.md
@@ -17,6 +17,12 @@ Although cards can support multiple actions, UI controls, and an overflow menu,
{{"demo": "pages/demos/cards/SimpleCard.js"}}
+## Complex Interaction
+
+On desktop, card content can expand.
+
+{{"demo": "pages/demos/cards/RecipeReviewCard.js"}}
+
## Media
Example of a card using an image to reinforce the content.
@@ -34,9 +40,3 @@ Supplemental actions within the card are explicitly called out using icons, text
Here's an example of a media control card.
{{"demo": "pages/demos/cards/MediaControlCard.js"}}
-
-## Complex Interaction
-
-On desktop, card content can expand.
-
-{{"demo": "pages/demos/cards/RecipeReviewCard.js"}}
diff --git a/docs/src/pages/demos/chips/chips.md b/docs/src/pages/demos/chips/chips.md
index d5938d9f6c6a9d..cf120570472804 100644
--- a/docs/src/pages/demos/chips/chips.md
+++ b/docs/src/pages/demos/chips/chips.md
@@ -31,10 +31,6 @@ Outlined chips offer an alternative style.
{{"demo": "pages/demos/chips/OutlinedChips.js"}}
-## Chip Playground
-
-{{"demo": "pages/demos/chips/ChipsPlayground.js"}}
-
## Chip array
An example of rendering multiple Chips from an array of values.
@@ -43,3 +39,7 @@ Deleting a chip removes it from the array. Note that since no
gain depth while clicked or touched.
{{"demo": "pages/demos/chips/ChipsArray.js"}}
+
+## Chip Playground
+
+{{"demo": "pages/demos/chips/ChipsPlayground.js"}}
diff --git a/docs/src/pages/demos/dialogs/AlertDialog.js b/docs/src/pages/demos/dialogs/AlertDialog.js
index ab2e9f3504b6fe..eb3eb5a9fefacf 100644
--- a/docs/src/pages/demos/dialogs/AlertDialog.js
+++ b/docs/src/pages/demos/dialogs/AlertDialog.js
@@ -22,7 +22,9 @@ class AlertDialog extends React.Component {
render() {
return (
-
Open alert dialog
+
+ Open alert dialog
+
- Slide in alert dialog
+
+ Slide in alert dialog
+
({
+ root: {
+ borderBottom: `1px solid ${theme.palette.divider}`,
+ margin: 0,
+ padding: theme.spacing.unit * 2,
+ },
+ closeButton: {
+ position: 'absolute',
+ right: theme.spacing.unit,
+ top: theme.spacing.unit,
+ color: theme.palette.grey[500],
+ },
+}))(props => {
+ const { children, classes, onClose } = props;
+ return (
+
+ {children}
+ {onClose ? (
+
+
+
+ ) : null}
+
+ );
+});
+
+const DialogContent = withStyles(theme => ({
+ root: {
+ margin: 0,
+ padding: theme.spacing.unit * 2,
+ },
+}))(MuiDialogContent);
+
+const DialogActions = withStyles(theme => ({
+ root: {
+ borderTop: `1px solid ${theme.palette.divider}`,
+ margin: 0,
+ padding: theme.spacing.unit,
+ },
+}))(MuiDialogActions);
+
+class CustomizedDialogDemo extends React.Component {
+ state = {
+ open: false,
+ };
+
+ handleClickOpen = () => {
+ this.setState({
+ open: true,
+ });
+ };
+
+ handleClose = () => {
+ this.setState({ open: false });
+ };
+
+ render() {
+ return (
+
+
+ Open dialog
+
+
+
+ Modal title
+
+
+
+ Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac
+ facilisis in, egestas eget quam. Morbi leo risus, porta ac consectetur ac, vestibulum
+ at eros.
+
+
+ Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vivamus sagittis
+ lacus vel augue laoreet rutrum faucibus dolor auctor.
+
+
+ Aenean lacinia bibendum nulla sed consectetur. Praesent commodo cursus magna, vel
+ scelerisque nisl consectetur et. Donec sed odio dui. Donec ullamcorper nulla non metus
+ auctor fringilla.
+
+
+
+
+ Save changes
+
+
+
+
+ );
+ }
+}
+
+export default CustomizedDialogDemo;
diff --git a/docs/src/pages/demos/dialogs/DraggableDialog.js b/docs/src/pages/demos/dialogs/DraggableDialog.js
new file mode 100644
index 00000000000000..04741b00ee1392
--- /dev/null
+++ b/docs/src/pages/demos/dialogs/DraggableDialog.js
@@ -0,0 +1,65 @@
+import React from 'react';
+import Button from '@material-ui/core/Button';
+import Dialog from '@material-ui/core/Dialog';
+import DialogActions from '@material-ui/core/DialogActions';
+import DialogContent from '@material-ui/core/DialogContent';
+import DialogContentText from '@material-ui/core/DialogContentText';
+import DialogTitle from '@material-ui/core/DialogTitle';
+import Paper from '@material-ui/core/Paper';
+import Draggable from 'react-draggable';
+
+function PaperComponent(props) {
+ return (
+
+
+
+ );
+}
+
+class DraggableDialog extends React.Component {
+ state = {
+ open: false,
+ };
+
+ handleClickOpen = () => {
+ this.setState({ open: true });
+ };
+
+ handleClose = () => {
+ this.setState({ open: false });
+ };
+
+ render() {
+ return (
+
+
+ Open form dialog
+
+
+ Subscribe
+
+
+ To subscribe to this website, please enter your email address here. We will send
+ updates occasionally.
+
+
+
+
+ Cancel
+
+
+ Subscribe
+
+
+
+
+ );
+ }
+}
+
+export default DraggableDialog;
diff --git a/docs/src/pages/demos/dialogs/FormDialog.js b/docs/src/pages/demos/dialogs/FormDialog.js
index c1599f3dec9151..8b2d2dc1970e16 100644
--- a/docs/src/pages/demos/dialogs/FormDialog.js
+++ b/docs/src/pages/demos/dialogs/FormDialog.js
@@ -23,7 +23,9 @@ export default class FormDialog extends React.Component {
render() {
return (
-
Open form dialog
+
+ Open form dialog
+
- Open full-screen dialog
+
+ Open full-screen dialog
+
({
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ margin: 'auto',
+ width: 'fit-content',
+ },
+ formControl: {
+ marginTop: theme.spacing.unit * 2,
+ minWidth: 120,
+ },
+ formControlLabel: {
+ marginTop: theme.spacing.unit,
+ },
+});
+
+class MaxWidthDialog extends React.Component {
+ state = {
+ open: false,
+ fullWidth: true,
+ maxWidth: 'sm',
+ };
+
+ handleClickOpen = () => {
+ this.setState({ open: true });
+ };
+
+ handleClose = () => {
+ this.setState({ open: false });
+ };
+
+ handleMaxWidthChange = event => {
+ this.setState({ maxWidth: event.target.value });
+ };
+
+ handleFullWidthChange = event => {
+ this.setState({ fullWidth: event.target.checked });
+ };
+
+ render() {
+ const { classes } = this.props;
+
+ return (
+
+
+ Open max-width dialog
+
+
+ Optional sizes
+
+
+ You can set my maximum width and whether to adapt or not.
+
+
+
+
+
+ Close
+
+
+
+
+ );
+ }
+}
+
+MaxWidthDialog.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(MaxWidthDialog);
diff --git a/docs/src/pages/demos/dialogs/ResponsiveDialog.js b/docs/src/pages/demos/dialogs/ResponsiveDialog.js
index 88dc531bf873fd..c15ac8773607d4 100644
--- a/docs/src/pages/demos/dialogs/ResponsiveDialog.js
+++ b/docs/src/pages/demos/dialogs/ResponsiveDialog.js
@@ -26,7 +26,9 @@ class ResponsiveDialog extends React.Component {
return (
- Open responsive dialog
+
+ Open responsive dialog
+
Selected: {this.state.selectedValue}
- Open simple dialog
+
+ Open simple dialog
+
({
width: 500,
height: 450,
},
- subheader: {
- width: '100%',
- },
});
/**
diff --git a/docs/src/pages/demos/grid-list/grid-list.md b/docs/src/pages/demos/grid-list/grid-list.md
index 9a32a4fb4a1ad4..26b457887a63fb 100644
--- a/docs/src/pages/demos/grid-list/grid-list.md
+++ b/docs/src/pages/demos/grid-list/grid-list.md
@@ -22,14 +22,6 @@ The overlay can accommodate a `title`, `subtitle` and secondary action - in this
{{"demo": "pages/demos/grid-list/TitlebarGridList.js", "hideEditButton": true}}
-## Advanced Grid list
-
-This example demonstrates "featured" tiles, using the `rows` and `cols` props to adjust the size of the tile, and the `padding` prop to adjust the spacing.
-The tiles have a customized titlebar, positioned at the top and with a custom gradient `titleBackground`.
-The secondary action `IconButton` is positioned on the left.
-
-{{"demo": "pages/demos/grid-list/AdvancedGridList.js", "hideEditButton": true}}
-
## Single line Grid list
This example demonstrates a horizontal scrollable single-line grid list of images.
@@ -37,3 +29,11 @@ Horizontally scrolling grid lists are discouraged because the scrolling interfer
One notable exception is a horizontally-scrolling, single-line grid list of images, such as a gallery.
{{"demo": "pages/demos/grid-list/SingleLineGridList.js", "hideEditButton": true}}
+
+## Advanced Grid list
+
+This example demonstrates "featured" tiles, using the `rows` and `cols` props to adjust the size of the tile, and the `padding` prop to adjust the spacing.
+The tiles have a customized titlebar, positioned at the top and with a custom gradient `titleBackground`.
+The secondary action `IconButton` is positioned on the left.
+
+{{"demo": "pages/demos/grid-list/AdvancedGridList.js", "hideEditButton": true}}
diff --git a/docs/src/pages/demos/lists/lists.md b/docs/src/pages/demos/lists/lists.md
index 72cea7455f1028..f866abba9db237 100644
--- a/docs/src/pages/demos/lists/lists.md
+++ b/docs/src/pages/demos/lists/lists.md
@@ -29,31 +29,24 @@ function ListItemLink(props) {
You can find a [demo with React Router following this section](/guides/composition/#react-router) of the documentation.
+## Nested List
+
+{{"demo": "pages/demos/lists/NestedList.js"}}
+
## Folder List
{{"demo": "pages/demos/lists/FolderList.js"}}
-## Inset List
-
-{{"demo": "pages/demos/lists/InsetList.js"}}
+## Interactive
-## Nested List
+Below is an interactive demo that lets you explore the visual results of the different settings:
-{{"demo": "pages/demos/lists/NestedList.js"}}
+{{"demo": "pages/demos/lists/InteractiveList.js"}}
## Selected ListItem
{{"demo": "pages/demos/lists/SelectedListItem.js"}}
-## Pinned Subheader List
-
-Upon scrolling, subheaders remain pinned to the top of the screen until pushed off screen by the next subheader.
-
-This feature is relying on the CSS sticky positioning.
-Unfortunately it's [not implemented](https://caniuse.com/#search=sticky) by all the browsers we are supporting. We default to `disableSticky` when not supported.
-
-{{"demo": "pages/demos/lists/PinnedSubheaderList.js"}}
-
## Align list items
You should change the list item alignment when displaying 3 lines or more, set the `alignItems="flex-start"` property.
@@ -80,8 +73,15 @@ The switch is the secondary action and a separate target.
{{"demo": "pages/demos/lists/SwitchListSecondary.js"}}
-## Interactive
+## Pinned Subheader List
-Below is an interactive demo that lets you explore the visual results of the different settings:
+Upon scrolling, subheaders remain pinned to the top of the screen until pushed off screen by the next subheader.
-{{"demo": "pages/demos/lists/InteractiveList.js"}}
+This feature is relying on the CSS sticky positioning.
+Unfortunately it's [not implemented](https://caniuse.com/#search=sticky) by all the browsers we are supporting. We default to `disableSticky` when not supported.
+
+{{"demo": "pages/demos/lists/PinnedSubheaderList.js"}}
+
+## Inset List
+
+{{"demo": "pages/demos/lists/InsetList.js"}}
diff --git a/docs/src/pages/demos/menus/menus.md b/docs/src/pages/demos/menus/menus.md
index 36cbc3851c324b..5c6f0115b32315 100644
--- a/docs/src/pages/demos/menus/menus.md
+++ b/docs/src/pages/demos/menus/menus.md
@@ -11,7 +11,7 @@ A [Menu](https://material.io/design/components/menus.html) displays a list of ch
## Simple Menu
-Simple menus open over the anchor element by default (this option can be changed via props). When close to a screen edge, simple menus vertically realign to make all menu items are completely visible.
+Simple menus open over the anchor element by default (this option can be changed via props). When close to a screen edge, simple menus vertically realign to make sure that all menu items are completely visible.
Choosing an option should immediately ideally commit the option and close the menu.
@@ -26,14 +26,6 @@ The currently selected menu item is set using the `selected` property (from [Lis
{{"demo": "pages/demos/menus/SimpleListMenu.js"}}
-If text in a simple menu wraps to a second line, use a simple dialog instead. Simple dialogs can have rows with varying heights.
-
-## Max height menus
-
-If the height of a menu prevents all menu items from being displayed, the menu can scroll internally.
-
-{{"demo": "pages/demos/menus/LongMenu.js"}}
-
## MenuList composition
The `Menu` component uses the `Popover` component internally.
@@ -46,16 +38,22 @@ The primary responsibility of the `MenuList` component is to handle the focus.
## Customized MenuItem
-The `MenuItem` is a wrapper around `ListItem` with some additional styles.
-You can use the same list composition features with the `MenuItem` component:
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in,
+here is one example of how you can customize the `MenuItem`.
+
+⚠️ While the material design specification encourages theming, this example is off the beaten path.
{{"demo": "pages/demos/menus/ListItemComposition.js"}}
-## Change Transition
+The `MenuItem` is a wrapper around `ListItem` with some additional styles.
+You can use the same list composition features with the `MenuItem` component:
-Use a different transition altogether.
+## Max height menus
-{{"demo": "pages/demos/menus/FadeMenu.js"}}
+If the height of a menu prevents all menu items from being displayed, the menu can scroll internally.
+
+{{"demo": "pages/demos/menus/LongMenu.js"}}
## Render Props
@@ -71,6 +69,12 @@ You can use the `Typography` component to workaround this issue:
{{"demo": "pages/demos/menus/TypographyMenu.js"}}
+## Change Transition
+
+Use a different transition altogether.
+
+{{"demo": "pages/demos/menus/FadeMenu.js"}}
+
## Complementary projects
For more advanced use cases you might be able to take advantage of:
diff --git a/docs/src/pages/demos/pickers/pickers.md b/docs/src/pages/demos/pickers/pickers.md
index eef3aff4487ec8..f22babe69af831 100644
--- a/docs/src/pages/demos/pickers/pickers.md
+++ b/docs/src/pages/demos/pickers/pickers.md
@@ -27,14 +27,14 @@ A native date picker example with `type="date"`, it can be used as a calendar to
{{"demo": "pages/demos/pickers/DatePickers.js"}}
-## Time pickers
-
-A native time picker example with `type="time"`:
-
-{{"demo": "pages/demos/pickers/TimePickers.js"}}
-
## Date & Time pickers
A native date & time picker example with `type="datetime-local"`:
{{"demo": "pages/demos/pickers/DateAndTimePickers.js"}}
+
+## Time pickers
+
+A native time picker example with `type="time"`:
+
+{{"demo": "pages/demos/pickers/TimePickers.js"}}
diff --git a/docs/src/pages/demos/progress/progress.md b/docs/src/pages/demos/progress/progress.md
index 7eef4c4a1e53ca..9eef8af5abdfcf 100644
--- a/docs/src/pages/demos/progress/progress.md
+++ b/docs/src/pages/demos/progress/progress.md
@@ -81,6 +81,17 @@ function Progress(props) {
}
```
+## Customized Progress
+
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in,
+here is one example of how you can customize the components.
+The last demo demonstrates how you can build a Facebook like spinner.
+
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
+{{"demo": "pages/demos/progress/CustomizedProgress.js"}}
+
## Delaying appearance
There are [3 important limits](https://www.nngroup.com/articles/response-times-3-important-limits/) to know around response time.
@@ -90,12 +101,6 @@ After 1.0 second, you can display a loader to keep user's flow of thought uninte
{{"demo": "pages/demos/progress/DelayingAppearance.js"}}
-## Customized Progress
-
-The last demo demonstrates how you can build a Facebook like spinner.
-
-{{"demo": "pages/demos/progress/CustomizedProgress.js"}}
-
## Limitations
Under heavy load, you might lose the stroke dash animation or see random CircularProgress ring widths.
diff --git a/docs/src/pages/demos/selection-controls/selection-controls.md b/docs/src/pages/demos/selection-controls/selection-controls.md
index 18ea7d59c22d91..2c312c4c9e2dcd 100644
--- a/docs/src/pages/demos/selection-controls/selection-controls.md
+++ b/docs/src/pages/demos/selection-controls/selection-controls.md
@@ -11,10 +11,29 @@ components: FormControl, FormGroup, FormLabel, FormControlLabel, RadioGroup, Che
Three types of selection controls are covered in this section:
-- **[Checkboxes](#checkboxes)** allow the selection of multiple options from a set.
- **[Radio Buttons](#radio-buttons)** allow the selection of a single option from a set.
+- **[Checkboxes](#checkboxes)** allow the selection of multiple options from a set.
- **[Switches](#switches)** allow a selection to be turned on or off.
+## Radio Buttons
+
+[Radio buttons](https://material.io/design/components/selection-controls.html#radio-buttons)
+allow the user to select one option from a set.
+Use radio buttons when the user needs to see all available options.
+If available options can be collapsed, consider using a dropdown menu because it uses less space.
+
+Radio buttons should have the most commonly used option selected by default.
+
+`RadioGroup` is a helpful wrapper used to group `Radio` components that provides an easier API, and proper keyboard accessibility to the group.
+
+{{"demo": "pages/demos/selection-controls/RadioButtonsGroup.js"}}
+
+### Standalone Radio Buttons
+
+`Radio` can also be used standalone, without the wrapper.
+
+{{"demo": "pages/demos/selection-controls/RadioButtons.js"}}
+
## Checkboxes
[Checkboxes](https://material.io/design/components/selection-controls.html#checkboxes)
@@ -37,25 +56,6 @@ If you have a single option, avoid using a checkbox and use an on/off switch ins
{{"demo": "pages/demos/selection-controls/CheckboxesGroup.js"}}
-## Radio Buttons
-
-[Radio buttons](https://material.io/design/components/selection-controls.html#radio-buttons)
-allow the user to select one option from a set.
-Use radio buttons when the user needs to see all available options.
-If available options can be collapsed, consider using a dropdown menu because it uses less space.
-
-Radio buttons should have the most commonly used option selected by default.
-
-`RadioGroup` is a helpful wrapper used to group `Radio` components that provides an easier API, and proper keyboard accessibility to the group.
-
-{{"demo": "pages/demos/selection-controls/RadioButtonsGroup.js"}}
-
-### Standalone Radio Buttons
-
-`Radio` can also be used standalone, without the wrapper.
-
-{{"demo": "pages/demos/selection-controls/RadioButtons.js"}}
-
## Switches
[Switches](https://material.io/design/components/selection-controls.html#switches)
@@ -84,6 +84,8 @@ However, we encourage you to use a [Checkbox](#checkboxes) instead.
If you have been reading the [overrides documentation page](/customization/overrides/)
but you are not confident jumping in, here's an example of how you can change the color of a Switch, and an iOS style Switch.
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
{{"demo": "pages/demos/selection-controls/CustomizedSwitches.js"}}
## Label placement
@@ -91,3 +93,17 @@ but you are not confident jumping in, here's an example of how you can change th
You can change the placement of the label:
{{"demo": "pages/demos/selection-controls/FormControlLabelPosition.js"}}
+
+## Accessibility
+
+All form controls should have a label to identify it, this includes radio buttons, checkboxes, and switches. In most cases, this is done by using the `` element ([FormControlLabel](/api/form-control-label/)).
+
+When a label can't be used, it's necessary to add an attribute directly to the input component.
+In this case, you can apply the additional attribute (e.g. `aria-label`, `aria-labelledby`, `title`) via the `inputProps` property.
+
+```jsx
+
+```
diff --git a/docs/src/pages/demos/selects/MultipleSelect.js b/docs/src/pages/demos/selects/MultipleSelect.js
index f208951bb26c42..ba27de01e68830 100644
--- a/docs/src/pages/demos/selects/MultipleSelect.js
+++ b/docs/src/pages/demos/selects/MultipleSelect.js
@@ -75,6 +75,19 @@ class MultipleSelect extends React.Component {
this.setState({ name: event.target.value });
};
+ handleChangeMultiple = event => {
+ const { options } = event.target;
+ const value = [];
+ for (let i = 0, l = options.length; i < l; i += 1) {
+ if (options[i].selected) {
+ value.push(options[i].value);
+ }
+ }
+ this.setState({
+ name: value,
+ });
+ };
+
render() {
const { classes } = this.props;
@@ -163,6 +176,26 @@ class MultipleSelect extends React.Component {
))}
+
+
+ Native
+
+
+ {names.map(name => (
+
+ {name}
+
+ ))}
+
+
);
}
diff --git a/docs/src/pages/demos/selects/selects.md b/docs/src/pages/demos/selects/selects.md
index 441f1c3b0d13a7..09472a842f70f8 100644
--- a/docs/src/pages/demos/selects/selects.md
+++ b/docs/src/pages/demos/selects/selects.md
@@ -29,6 +29,10 @@ Like with the single selection, you can pull out the new value by accessing `eve
{{"demo": "pages/demos/selects/MultipleSelect.js"}}
+## Controlled open Select
+
+{{"demo": "pages/demos/selects/ControlledOpenSelect.js"}}
+
## With a Dialog
While it's discouraged by the Material Design specification, you can use a select inside a dialog.
@@ -38,7 +42,3 @@ While it's discouraged by the Material Design specification, you can use a selec
## Text Fields
The `TextField` wrapper component is a complete form control including a label, input and help text. You can find an example with the select mode [in this section](/demos/text-fields/#textfield).
-
-## Controlled open Select
-
-{{"demo": "pages/demos/selects/ControlledOpenSelect.js"}}
diff --git a/docs/src/pages/demos/snackbars/ConsecutiveSnackbars.js b/docs/src/pages/demos/snackbars/ConsecutiveSnackbars.js
index dfced170592a52..a0ac3b271ae20f 100644
--- a/docs/src/pages/demos/snackbars/ConsecutiveSnackbars.js
+++ b/docs/src/pages/demos/snackbars/ConsecutiveSnackbars.js
@@ -57,13 +57,14 @@ class ConsecutiveSnackbars extends React.Component {
render() {
const { classes } = this.props;
- const { message, key } = this.state.messageInfo;
+ const { messageInfo } = this.state;
+
return (
Show message A
Show message B
{message}}
+ message={{messageInfo.message} }
action={[
UNDO
diff --git a/docs/src/pages/demos/snackbars/IntegrationNotistack.js b/docs/src/pages/demos/snackbars/IntegrationNotistack.js
new file mode 100644
index 00000000000000..941cf0df5246b9
--- /dev/null
+++ b/docs/src/pages/demos/snackbars/IntegrationNotistack.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Button from '@material-ui/core/Button';
+import { SnackbarProvider, withSnackbar } from 'notistack';
+
+class App extends React.Component {
+ handleClick = () => {
+ this.props.enqueueSnackbar('I love snacks.');
+ };
+
+ handleClickVariant = variant => () => {
+ // variant could be success, error, warning or info
+ this.props.enqueueSnackbar('This is a warning message!', { variant });
+ };
+
+ render() {
+ return (
+
+ Show snackbar
+ Show warning snackbar
+
+ );
+ }
+}
+
+App.propTypes = {
+ enqueueSnackbar: PropTypes.func.isRequired,
+};
+
+const MyApp = withSnackbar(App);
+
+function IntegrationNotistack() {
+ return (
+
+
+
+ );
+}
+
+export default IntegrationNotistack;
diff --git a/docs/src/pages/demos/snackbars/snackbars.md b/docs/src/pages/demos/snackbars/snackbars.md
index 09e220b9b1afff..e18599ec29a3ba 100644
--- a/docs/src/pages/demos/snackbars/snackbars.md
+++ b/docs/src/pages/demos/snackbars/snackbars.md
@@ -22,11 +22,15 @@ A basic snackbar that aims to reproduce Google Keep's snackbar behavior.
{{"demo": "pages/demos/snackbars/SimpleSnackbar.js"}}
-## Message Length
+## Customized Snackbars
-Some snackbars with varying message length.
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in,
+here are examples of how you can change the look of a Snackbar.
-{{"demo": "pages/demos/snackbars/LongTextSnackbar.js"}}
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
+{{"demo": "pages/demos/snackbars/CustomizedSnackbars.js"}}
## Positioned
@@ -34,19 +38,19 @@ There may be circumstances when the placement of the snackbar needs to be more f
{{"demo": "pages/demos/snackbars/PositionedSnackbar.js"}}
-## Transitions
+## Message Length
-### Control Direction
+Some snackbars with varying message length.
-Change the direction of the transition. Slide is the default transition.
+{{"demo": "pages/demos/snackbars/LongTextSnackbar.js"}}
-{{"demo": "pages/demos/snackbars/DirectionSnackbar.js"}}
+## Transitions
-### Change Transition
+### Consecutive Snackbars
-Use a different transition all together.
+Per [Google's guidelines](https://material.io/design/components/snackbars.html#snackbars-toasts-usage), when a second snackbar is triggered while the first is displayed, the first should start the contraction motion downwards before the second one animates upwards.
-{{"demo": "pages/demos/snackbars/FadeSnackbar.js"}}
+{{"demo": "pages/demos/snackbars/ConsecutiveSnackbars.js"}}
### Don't block the floating action button
@@ -54,22 +58,27 @@ Move the floating action button vertically to accommodate the snackbar height.
{{"demo": "pages/demos/snackbars/FabIntegrationSnackbar.js"}}
-### Consecutive Snackbars
+### Control Direction
-Per [Google's guidelines](https://material.io/design/components/snackbars.html#snackbars-toasts-usage), when a second snackbar is triggered while the first is displayed, the first should start the contraction motion downwards before the second one animates upwards.
+Change the direction of the transition. Slide is the default transition.
-{{"demo": "pages/demos/snackbars/ConsecutiveSnackbars.js"}}
+{{"demo": "pages/demos/snackbars/DirectionSnackbar.js"}}
-## Customized Snackbars
+### Change Transition
-If you have been reading the [overrides documentation page](/customization/overrides/)
-but you are not confident jumping in,
-here are examples of how you can change the look of a Snackbar.
+Use a different transition all together.
-{{"demo": "pages/demos/snackbars/CustomizedSnackbars.js"}}
+{{"demo": "pages/demos/snackbars/FadeSnackbar.js"}}
## Complementary projects
For more advanced use cases you might be able to take advantage of:
-- [notistack](https://github.com/iamhosseindhv/notistack) Highly customisable notification snackbars that can be stacked on top of each other.
+### notistack
+
+
+
+
+In the following example, we demonstrate how to use [notistack](https://github.com/iamhosseindhv/notistack). notistack makes it easy to display snackbars (so you don't have to deal with open/close state of them). It also enables you to stack them on top of one another.
+
+{{"demo": "pages/demos/snackbars/IntegrationNotistack.js"}}
diff --git a/docs/src/pages/demos/steppers/CustomizedStepper.js b/docs/src/pages/demos/steppers/CustomizedStepper.js
index b840f69922fe2f..02e1a8ff32b5b2 100644
--- a/docs/src/pages/demos/steppers/CustomizedStepper.js
+++ b/docs/src/pages/demos/steppers/CustomizedStepper.js
@@ -114,7 +114,7 @@ class CustomizedStepper extends React.Component {
{activeStep === steps.length ? (
- All steps completed - you"re finished
+ All steps completed - you're finished
Reset
diff --git a/docs/src/pages/demos/steppers/HorizontalLinearStepper.js b/docs/src/pages/demos/steppers/HorizontalLinearStepper.js
index 03d555cc571151..83b2f8a3d33da2 100644
--- a/docs/src/pages/demos/steppers/HorizontalLinearStepper.js
+++ b/docs/src/pages/demos/steppers/HorizontalLinearStepper.js
@@ -122,7 +122,7 @@ class HorizontalLinearStepper extends React.Component {
{activeStep === steps.length ? (
- All steps completed - you"re finished
+ All steps completed - you're finished
Reset
diff --git a/docs/src/pages/demos/steppers/HorizontalNonLinearAlternativeLabelStepper.js b/docs/src/pages/demos/steppers/HorizontalNonLinearAlternativeLabelStepper.js
index ca56f0393f574f..c1a4b687cc07da 100644
--- a/docs/src/pages/demos/steppers/HorizontalNonLinearAlternativeLabelStepper.js
+++ b/docs/src/pages/demos/steppers/HorizontalNonLinearAlternativeLabelStepper.js
@@ -188,7 +188,7 @@ class HorizontalNonLinearAlternativeLabelStepper extends React.Component {
{this.allStepsCompleted() ? (
- All steps completed - you"re finished
+ All steps completed - you're finished
Reset
diff --git a/docs/src/pages/demos/steppers/HorizontalNonLinearStepper.js b/docs/src/pages/demos/steppers/HorizontalNonLinearStepper.js
index d6055b95427fe0..c770da501c6b7a 100644
--- a/docs/src/pages/demos/steppers/HorizontalNonLinearStepper.js
+++ b/docs/src/pages/demos/steppers/HorizontalNonLinearStepper.js
@@ -131,7 +131,7 @@ class HorizontalNonLinearStepper extends React.Component {
{this.allStepsCompleted() ? (
- All steps completed - you"re finished
+ All steps completed - you're finished
Reset
diff --git a/docs/src/pages/demos/steppers/HorizontalNonLinearStepperWithError.js b/docs/src/pages/demos/steppers/HorizontalNonLinearStepperWithError.js
index 129d6e9298a407..f31ff06dd82efd 100644
--- a/docs/src/pages/demos/steppers/HorizontalNonLinearStepperWithError.js
+++ b/docs/src/pages/demos/steppers/HorizontalNonLinearStepperWithError.js
@@ -133,7 +133,7 @@ class HorizontalNonLinearStepperWithError extends React.Component {
{activeStep === steps.length ? (
- All steps completed - you"re finished
+ All steps completed - you're finished
Reset
diff --git a/docs/src/pages/demos/steppers/VerticalLinearStepper.js b/docs/src/pages/demos/steppers/VerticalLinearStepper.js
index f4e8bc4a574378..1a8835882c7a2c 100644
--- a/docs/src/pages/demos/steppers/VerticalLinearStepper.js
+++ b/docs/src/pages/demos/steppers/VerticalLinearStepper.js
@@ -110,7 +110,7 @@ class VerticalLinearStepper extends React.Component {
{activeStep === steps.length && (
- All steps completed - you"re finished
+ All steps completed - you're finished
Reset
diff --git a/docs/src/pages/demos/steppers/steppers.md b/docs/src/pages/demos/steppers/steppers.md
index a89dd334c64b33..6a9fbe92c37bef 100644
--- a/docs/src/pages/demos/steppers/steppers.md
+++ b/docs/src/pages/demos/steppers/steppers.md
@@ -64,8 +64,14 @@ Labels can be placed below the step icon by setting the `alternativeLabel` prope
## Customized Stepper
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in,
+here are examples of how you can change the look of a stepper.
+
This component uses a customized `StepConnector` element that changes border color based on the `active` and `completed` state.
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
{{"demo": "pages/demos/steppers/CustomizedStepper.js"}}
## Mobile Stepper
diff --git a/docs/src/pages/demos/tables/CustomPaginationActionsTable.js b/docs/src/pages/demos/tables/CustomPaginationActionsTable.js
index 43489626e1bfd4..5c2f1ef515d1d2 100644
--- a/docs/src/pages/demos/tables/CustomPaginationActionsTable.js
+++ b/docs/src/pages/demos/tables/CustomPaginationActionsTable.js
@@ -157,8 +157,8 @@ class CustomPaginationActionsTable extends React.Component {
{row.name}
- {row.calories}
- {row.fat}
+ {row.calories}
+ {row.fat}
);
})}
@@ -176,6 +176,9 @@ class CustomPaginationActionsTable extends React.Component {
count={rows.length}
rowsPerPage={rowsPerPage}
page={page}
+ SelectProps={{
+ native: true,
+ }}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
ActionsComponent={TablePaginationActionsWrapped}
diff --git a/docs/src/pages/demos/tables/CustomizedTable.js b/docs/src/pages/demos/tables/CustomizedTable.js
index 59dde98d5c56ee..c192eb73c3e53d 100644
--- a/docs/src/pages/demos/tables/CustomizedTable.js
+++ b/docs/src/pages/demos/tables/CustomizedTable.js
@@ -57,10 +57,10 @@ function CustomizedTable(props) {
Dessert (100g serving)
- Calories
- Fat (g)
- Carbs (g)
- Protein (g)
+ Calories
+ Fat (g)
+ Carbs (g)
+ Protein (g)
@@ -70,10 +70,10 @@ function CustomizedTable(props) {
{row.name}
- {row.calories}
- {row.fat}
- {row.carbs}
- {row.protein}
+ {row.calories}
+ {row.fat}
+ {row.carbs}
+ {row.protein}
);
})}
diff --git a/docs/src/pages/demos/tables/EnhancedTable.js b/docs/src/pages/demos/tables/EnhancedTable.js
index bf20897d533209..8d309cfbcdcfb8 100644
--- a/docs/src/pages/demos/tables/EnhancedTable.js
+++ b/docs/src/pages/demos/tables/EnhancedTable.js
@@ -79,7 +79,7 @@ class EnhancedTableHead extends React.Component {
return (
@@ -312,10 +312,10 @@ class EnhancedTable extends React.Component {
{n.name}
- {n.calories}
- {n.fat}
- {n.carbs}
- {n.protein}
+ {n.calories}
+ {n.fat}
+ {n.carbs}
+ {n.protein}
);
})}
diff --git a/docs/src/pages/demos/tables/ReactVirtualizedTable.js b/docs/src/pages/demos/tables/ReactVirtualizedTable.js
new file mode 100644
index 00000000000000..568cbe01ef22a0
--- /dev/null
+++ b/docs/src/pages/demos/tables/ReactVirtualizedTable.js
@@ -0,0 +1,227 @@
+/* eslint-disable no-console */
+
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import TableCell from '@material-ui/core/TableCell';
+import TableSortLabel from '@material-ui/core/TableSortLabel';
+import Paper from '@material-ui/core/Paper';
+import { AutoSizer, Column, SortDirection, Table } from 'react-virtualized';
+
+const styles = theme => ({
+ table: {
+ fontFamily: theme.typography.fontFamily,
+ },
+ flexContainer: {
+ display: 'flex',
+ alignItems: 'center',
+ boxSizing: 'border-box',
+ },
+ tableRow: {
+ cursor: 'pointer',
+ },
+ tableRowHover: {
+ '&:hover': {
+ backgroundColor: theme.palette.grey[200],
+ },
+ },
+ tableCell: {
+ flex: 1,
+ },
+ noClick: {
+ cursor: 'initial',
+ },
+});
+
+class MuiVirtualizedTable extends React.PureComponent {
+ getRowClassName = ({ index }) => {
+ const { classes, rowClassName, onRowClick } = this.props;
+
+ return classNames(classes.tableRow, classes.flexContainer, rowClassName, {
+ [classes.tableRowHover]: index !== -1 && onRowClick != null,
+ });
+ };
+
+ cellRenderer = ({ cellData, columnIndex = null }) => {
+ const { columns, classes, rowHeight, onRowClick } = this.props;
+ return (
+
+ {cellData}
+
+ );
+ };
+
+ headerRenderer = ({ label, columnIndex, dataKey, sortBy, sortDirection }) => {
+ const { headerHeight, columns, classes, sort } = this.props;
+ const direction = {
+ [SortDirection.ASC]: 'asc',
+ [SortDirection.DESC]: 'desc',
+ };
+
+ const inner =
+ !columns[columnIndex].disableSort && sort != null ? (
+
+ {label}
+
+ ) : (
+ label
+ );
+
+ return (
+
+ {inner}
+
+ );
+ };
+
+ render() {
+ const { classes, columns, ...tableProps } = this.props;
+ return (
+
+ {({ height, width }) => (
+
+ {columns.map(({ cellContentRenderer = null, className, dataKey, ...other }, index) => {
+ let renderer;
+ if (cellContentRenderer != null) {
+ renderer = cellRendererProps =>
+ this.cellRenderer({
+ cellData: cellContentRenderer(cellRendererProps),
+ columnIndex: index,
+ });
+ } else {
+ renderer = this.cellRenderer;
+ }
+
+ return (
+
+ this.headerRenderer({
+ ...headerProps,
+ columnIndex: index,
+ })
+ }
+ className={classNames(classes.flexContainer, className)}
+ cellRenderer={renderer}
+ dataKey={dataKey}
+ {...other}
+ />
+ );
+ })}
+
+ )}
+
+ );
+ }
+}
+
+MuiVirtualizedTable.propTypes = {
+ classes: PropTypes.object.isRequired,
+ columns: PropTypes.arrayOf(
+ PropTypes.shape({
+ cellContentRenderer: PropTypes.func,
+ dataKey: PropTypes.string.isRequired,
+ width: PropTypes.number.isRequired,
+ }),
+ ).isRequired,
+ headerHeight: PropTypes.number,
+ onRowClick: PropTypes.func,
+ rowClassName: PropTypes.string,
+ rowHeight: PropTypes.oneOfType([PropTypes.number, PropTypes.func]),
+ sort: PropTypes.func,
+};
+
+MuiVirtualizedTable.defaultProps = {
+ headerHeight: 56,
+ rowHeight: 56,
+};
+
+const WrappedVirtualizedTable = withStyles(styles)(MuiVirtualizedTable);
+
+const data = [
+ ['Frozen yoghurt', 159, 6.0, 24, 4.0],
+ ['Ice cream sandwich', 237, 9.0, 37, 4.3],
+ ['Eclair', 262, 16.0, 24, 6.0],
+ ['Cupcake', 305, 3.7, 67, 4.3],
+ ['Gingerbread', 356, 16.0, 49, 3.9],
+];
+
+let id = 0;
+function createData(dessert, calories, fat, carbs, protein) {
+ id += 1;
+ return { id, dessert, calories, fat, carbs, protein };
+}
+
+const rows = [];
+
+for (let i = 0; i < 200; i += 1) {
+ const randomSelection = data[Math.floor(Math.random() * data.length)];
+ rows.push(createData(...randomSelection));
+}
+
+function ReactVirtualizedTable() {
+ return (
+
+ rows[index]}
+ onRowClick={event => console.log(event)}
+ columns={[
+ {
+ width: 200,
+ flexGrow: 1.0,
+ label: 'Dessert',
+ dataKey: 'dessert',
+ },
+ {
+ width: 120,
+ label: 'Calories (g)',
+ dataKey: 'calories',
+ numeric: true,
+ },
+ {
+ width: 120,
+ label: 'Fat (g)',
+ dataKey: 'fat',
+ numeric: true,
+ },
+ {
+ width: 120,
+ label: 'Carbs (g)',
+ dataKey: 'carbs',
+ numeric: true,
+ },
+ {
+ width: 120,
+ label: 'Protein (g)',
+ dataKey: 'protein',
+ numeric: true,
+ },
+ ]}
+ />
+
+ );
+}
+
+export default ReactVirtualizedTable;
diff --git a/docs/src/pages/demos/tables/SimpleTable.js b/docs/src/pages/demos/tables/SimpleTable.js
index 640be4310091e6..d1eedba33461fa 100644
--- a/docs/src/pages/demos/tables/SimpleTable.js
+++ b/docs/src/pages/demos/tables/SimpleTable.js
@@ -42,10 +42,10 @@ function SimpleTable(props) {
Dessert (100g serving)
- Calories
- Fat (g)
- Carbs (g)
- Protein (g)
+ Calories
+ Fat (g)
+ Carbs (g)
+ Protein (g)
@@ -55,10 +55,10 @@ function SimpleTable(props) {
{row.name}
- {row.calories}
- {row.fat}
- {row.carbs}
- {row.protein}
+ {row.calories}
+ {row.fat}
+ {row.carbs}
+ {row.protein}
);
})}
diff --git a/docs/src/pages/demos/tables/SpanningTable.js b/docs/src/pages/demos/tables/SpanningTable.js
index 0a97c4f6c2d7c1..1e60bb2d28173a 100644
--- a/docs/src/pages/demos/tables/SpanningTable.js
+++ b/docs/src/pages/demos/tables/SpanningTable.js
@@ -56,9 +56,9 @@ function SpanningTable(props) {
Desc
- Qty.
- @
- Price
+ Qty.
+ @
+ Price
@@ -66,25 +66,25 @@ function SpanningTable(props) {
return (
{row.desc}
- {row.qty}
- {row.unit}
- {ccyFormat(row.price)}
+ {row.qty}
+ {row.unit}
+ {ccyFormat(row.price)}
);
})}
Subtotal
- {ccyFormat(invoiceSubtotal)}
+ {ccyFormat(invoiceSubtotal)}
Tax
- {`${(TAX_RATE * 100).toFixed(0)} %`}
- {ccyFormat(invoiceTaxes)}
+ {`${(TAX_RATE * 100).toFixed(0)} %`}
+ {ccyFormat(invoiceTaxes)}
Total
- {ccyFormat(invoiceTotal)}
+ {ccyFormat(invoiceTotal)}
diff --git a/docs/src/pages/demos/tables/tables.md b/docs/src/pages/demos/tables/tables.md
index 953571efa9f4d0..3a933e7f235db3 100644
--- a/docs/src/pages/demos/tables/tables.md
+++ b/docs/src/pages/demos/tables/tables.md
@@ -39,11 +39,15 @@ The Table has been given a fixed width to demonstrate horizontal scrolling. In o
{{"demo": "pages/demos/tables/EnhancedTable.js"}}
-## Spanning Table
+## Customized Tables
-A simple example with spanning rows & columns.
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in,
+here are examples of how you can change the look of a `TableCell`.
-{{"demo": "pages/demos/tables/SpanningTable.js"}}
+⚠️ While the material design specification encourages theming, this example is off the beaten path.
+
+{{"demo": "pages/demos/tables/CustomizedTable.js"}}
## Custom Table Pagination Action
@@ -52,11 +56,17 @@ custom actions.
{{"demo": "pages/demos/tables/CustomPaginationActionsTable.js"}}
-## Customized Tables
+## Spanning Table
-You can customize the look and feel of the table by overriding the styles of the `TableCell` component.
+A simple example with spanning rows & columns.
-{{"demo": "pages/demos/tables/CustomizedTable.js"}}
+{{"demo": "pages/demos/tables/SpanningTable.js"}}
+
+## Virtualized Table
+
+In the following example, we demonstrate how to use [react-virtualized](https://github.com/bvaughn/react-virtualized) with the `Table` component. It renders 200 rows and can easily handle more.
+
+{{"demo": "pages/demos/tables/ReactVirtualizedTable.js"}}
## Complementary projects
diff --git a/docs/src/pages/demos/tabs/tabs.md b/docs/src/pages/demos/tabs/tabs.md
index ee8346ec553f59..7098de7b43bd1e 100644
--- a/docs/src/pages/demos/tabs/tabs.md
+++ b/docs/src/pages/demos/tabs/tabs.md
@@ -64,13 +64,14 @@ Left and right scroll buttons will never be presented. All scrolling must be in
{{"demo": "pages/demos/tabs/ScrollableTabsButtonPrevent.js"}}
-## Icon Tabs
+## Customized Tabs
-Tab labels may be either all icons or all text.
+If you have read the [overrides documentation page](/customization/overrides/)
+but aren't confident jumping in, here's an example of how you can change the main color of the Tabs.
-{{"demo": "pages/demos/tabs/IconTabs.js"}}
+⚠️ While the material design specification encourages theming, this example is off the beaten path.
-{{"demo": "pages/demos/tabs/IconLabelTabs.js"}}
+{{"demo": "pages/demos/tabs/CustomizedTabs.js"}}
## Nav Tabs
@@ -78,9 +79,10 @@ By default tabs use a `button` element, but you can provide your own custom tag
{{"demo": "pages/demos/tabs/NavTabs.js"}}
-## Customized Tabs
+## Icon Tabs
-If you have read the [overrides documentation page](/customization/overrides/)
-but aren't confident jumping in, here's an example of how you can change the main color of the Tabs. The following demo matches the [Ant Design UI](https://ant.design/components/tabs/).
+Tab labels may be either all icons or all text.
-{{"demo": "pages/demos/tabs/CustomizedTabs.js"}}
+{{"demo": "pages/demos/tabs/IconTabs.js"}}
+
+{{"demo": "pages/demos/tabs/IconLabelTabs.js"}}
diff --git a/docs/src/pages/demos/text-fields/FormattedInputs.js b/docs/src/pages/demos/text-fields/FormattedInputs.js
index 7ac0e57e07fa54..e7608913bbafab 100644
--- a/docs/src/pages/demos/text-fields/FormattedInputs.js
+++ b/docs/src/pages/demos/text-fields/FormattedInputs.js
@@ -1,5 +1,3 @@
-/* eslint-disable react/prefer-stateless-function */
-
import React from 'react';
import MaskedInput from 'react-text-mask';
import NumberFormat from 'react-number-format';
diff --git a/docs/src/pages/demos/text-fields/text-fields.md b/docs/src/pages/demos/text-fields/text-fields.md
index ff2130f489bf0a..e2f13c3a214409 100644
--- a/docs/src/pages/demos/text-fields/text-fields.md
+++ b/docs/src/pages/demos/text-fields/text-fields.md
@@ -48,13 +48,14 @@ The component takes care of the most used properties, then it's up to the user t
{{"demo": "pages/demos/text-fields/Inputs.js"}}
-## Layout
+## Customized inputs
-`TextField`, `FormControl` allow the specification of `margin` to alter the vertical spacing of inputs. Using
-`none` (default) will not apply margins to the `FormControl`, whereas `dense` and `normal` will as well as alter
-other styles to meet the specification.
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in, here's an example of how you can change the main color of an Input.
-{{"demo": "pages/demos/text-fields/TextFieldMargins.js"}}
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
+{{"demo": "pages/demos/text-fields/CustomizedInputs.js"}}
## Input Adornments
@@ -64,35 +65,27 @@ For instance, you can use an icon button to hide or reveal the password.
{{"demo": "pages/demos/text-fields/InputAdornments.js"}}
-## Filled Input Adornments
-
-{{"demo": "pages/demos/text-fields/FilledInputAdornments.js"}}
+### With icon
-## Outlined Input Adornments
-
-{{"demo": "pages/demos/text-fields/OutlinedInputAdornments.js"}}
-
-## Formatted inputs
-
-You can use third-party libraries to format an input.
-You have to provide a custom implementation of the ` ` element with the `inputComponent` property.
+Icons can be specified as prepended or appended.
-The following demo uses the [react-text-mask](https://github.com/text-mask/text-mask) and [react-number-format](https://github.com/s-yadav/react-number-format) libraries.
+{{"demo": "pages/demos/text-fields/InputWithIcon.js"}}
-{{"demo": "pages/demos/text-fields/FormattedInputs.js"}}
+### Filled Input Adornments
-## Customized inputs
+{{"demo": "pages/demos/text-fields/FilledInputAdornments.js"}}
-If you have been reading the [overrides documentation page](/customization/overrides/)
-but you are not confident jumping in, here's an example of how you can change the main color of an Input.
+### Outlined Input Adornments
-{{"demo": "pages/demos/text-fields/CustomizedInputs.js"}}
+{{"demo": "pages/demos/text-fields/OutlinedInputAdornments.js"}}
-## With icon
+## Layout
-Icons can be specified as prepended or appended.
+`TextField`, `FormControl` allow the specification of `margin` to alter the vertical spacing of inputs. Using
+`none` (default) will not apply margins to the `FormControl`, whereas `dense` and `normal` will as well as alter
+other styles to meet the specification.
-{{"demo": "pages/demos/text-fields/InputWithIcon.js"}}
+{{"demo": "pages/demos/text-fields/TextFieldMargins.js"}}
## Limitations
@@ -111,6 +104,15 @@ or
Count
```
+## Formatted inputs
+
+You can use third-party libraries to format an input.
+You have to provide a custom implementation of the ` ` element with the `inputComponent` property.
+
+The following demo uses the [react-text-mask](https://github.com/text-mask/text-mask) and [react-number-format](https://github.com/s-yadav/react-number-format) libraries.
+
+{{"demo": "pages/demos/text-fields/FormattedInputs.js"}}
+
## Complementary projects
For more advanced use cases you might be able to take advantage of:
diff --git a/docs/src/pages/demos/tooltips/CustomizedTooltips.js b/docs/src/pages/demos/tooltips/CustomizedTooltips.js
index 448e56c2283ea0..61a753520c6cb0 100644
--- a/docs/src/pages/demos/tooltips/CustomizedTooltips.js
+++ b/docs/src/pages/demos/tooltips/CustomizedTooltips.js
@@ -4,15 +4,9 @@ import { withStyles } from '@material-ui/core/styles';
import Button from '@material-ui/core/Button';
import Tooltip from '@material-ui/core/Tooltip';
-const styles = theme => ({
- lightTooltip: {
- background: theme.palette.common.white,
- color: theme.palette.text.primary,
- boxShadow: theme.shadows[1],
- fontSize: 11,
- },
- arrowPopper: {
- '&[x-placement*="bottom"] $arrowArrow': {
+function arrowGenerator(color) {
+ return {
+ '&[x-placement*="bottom"] $arrow': {
top: 0,
left: 0,
marginTop: '-0.9em',
@@ -20,10 +14,10 @@ const styles = theme => ({
height: '1em',
'&::before': {
borderWidth: '0 1em 1em 1em',
- borderColor: `transparent transparent ${theme.palette.grey[700]} transparent`,
+ borderColor: `transparent transparent ${color} transparent`,
},
},
- '&[x-placement*="top"] $arrowArrow': {
+ '&[x-placement*="top"] $arrow': {
bottom: 0,
left: 0,
marginBottom: '-0.9em',
@@ -31,31 +25,44 @@ const styles = theme => ({
height: '1em',
'&::before': {
borderWidth: '1em 1em 0 1em',
- borderColor: `${theme.palette.grey[700]} transparent transparent transparent`,
+ borderColor: `${color} transparent transparent transparent`,
},
},
- '&[x-placement*="right"] $arrowArrow': {
+ '&[x-placement*="right"] $arrow': {
left: 0,
marginLeft: '-0.9em',
height: '3em',
width: '1em',
'&::before': {
borderWidth: '1em 1em 1em 0',
- borderColor: `transparent ${theme.palette.grey[700]} transparent transparent`,
+ borderColor: `transparent ${color} transparent transparent`,
},
},
- '&[x-placement*="left"] $arrowArrow': {
+ '&[x-placement*="left"] $arrow': {
right: 0,
marginRight: '-0.9em',
height: '3em',
width: '1em',
'&::before': {
borderWidth: '1em 0 1em 1em',
- borderColor: `transparent transparent transparent ${theme.palette.grey[700]}`,
+ borderColor: `transparent transparent transparent ${color}`,
},
},
+ };
+}
+
+const styles = theme => ({
+ button: {
+ margin: theme.spacing.unit,
+ },
+ lightTooltip: {
+ backgroundColor: theme.palette.common.white,
+ color: theme.palette.text.primary,
+ boxShadow: theme.shadows[1],
+ fontSize: 11,
},
- arrowArrow: {
+ arrowPopper: arrowGenerator(theme.palette.grey[700]),
+ arrow: {
position: 'absolute',
fontSize: 7,
width: '3em',
@@ -69,8 +76,21 @@ const styles = theme => ({
borderStyle: 'solid',
},
},
- button: {
- margin: theme.spacing.unit,
+ bootstrapPopper: arrowGenerator(theme.palette.common.black),
+ bootstrapTooltip: {
+ backgroundColor: theme.palette.common.black,
+ },
+ bootstrapPlacementLeft: {
+ margin: '0 8px',
+ },
+ bootstrapPlacementRight: {
+ margin: '0 8px',
+ },
+ bootstrapPlacementTop: {
+ margin: '8px 0',
+ },
+ bootstrapPlacementBottom: {
+ margin: '8px 0',
},
});
@@ -90,9 +110,6 @@ class CustomizedTooltips extends React.Component {
return (
-
- Default
-
Light
@@ -100,7 +117,7 @@ class CustomizedTooltips extends React.Component {
title={
Add
-
+
}
classes={{ popper: classes.arrowPopper }}
@@ -117,6 +134,34 @@ class CustomizedTooltips extends React.Component {
>
Arrow
+
+ Add
+
+
+ }
+ classes={{
+ tooltip: classes.bootstrapTooltip,
+ popper: classes.bootstrapPopper,
+ tooltipPlacementLeft: classes.bootstrapPlacementLeft,
+ tooltipPlacementRight: classes.bootstrapPlacementRight,
+ tooltipPlacementTop: classes.bootstrapPlacementTop,
+ tooltipPlacementBottom: classes.bootstrapPlacementBottom,
+ }}
+ PopperProps={{
+ popperOptions: {
+ modifiers: {
+ arrow: {
+ enabled: Boolean(this.state.arrowRef),
+ element: this.state.arrowRef,
+ },
+ },
+ },
+ }}
+ >
+ Bootstrap
+
);
}
diff --git a/docs/src/pages/demos/tooltips/tooltips.md b/docs/src/pages/demos/tooltips/tooltips.md
index e35f89991f34b6..7277056799bb37 100644
--- a/docs/src/pages/demos/tooltips/tooltips.md
+++ b/docs/src/pages/demos/tooltips/tooltips.md
@@ -20,11 +20,14 @@ They don’t have directional arrows; instead, they rely on motion emanating fro
{{"demo": "pages/demos/tooltips/PositionedTooltips.js"}}
-## Controlled Tooltips
+## Customized Tooltips
-You can use the `open`, `onOpen` and `onClose` properties to control the behavior of the tooltip.
+If you have been reading the [overrides documentation page](/customization/overrides/)
+but you are not confident jumping in, here's an example of how you can theme a tooltip.
-{{"demo": "pages/demos/tooltips/ControlledTooltips.js"}}
+⚠️ While the material design specification encourages theming, these examples are off the beaten path.
+
+{{"demo": "pages/demos/tooltips/CustomizedTooltips.js"}}
## Triggers
@@ -32,19 +35,23 @@ You can define the types of events that cause a tooltip to show.
{{"demo": "pages/demos/tooltips/TriggersTooltips.js"}}
-## Transitions
+## Controlled Tooltips
-Use a different transition.
+You can use the `open`, `onOpen` and `onClose` properties to control the behavior of the tooltip.
-{{"demo": "pages/demos/tooltips/TransitionsTooltips.js"}}
+{{"demo": "pages/demos/tooltips/ControlledTooltips.js"}}
-## Showing and hiding
+## Variable Width
-The tooltip is normally shown immediately when the user's mouse hovers over the element, and hides immediately when the user's mouse leaves. A delay in showing or hiding the tooltip can be added through the properties `enterDelay` and `leaveDelay`, as shown in the Controlled Tooltips demo above.
+The `Tooltip` wraps long text by default to make it readable.
-On mobile, the tooltip is displayed when the user longpresses the element and hides after a delay of 1500ms. You can disable this feature with the `disableTouchListener` property.
+{{"demo": "pages/demos/tooltips/VariableWidth.js"}}
-{{"demo": "pages/demos/tooltips/DelayTooltips.js"}}
+## Interactive
+
+A tooltip can be interactive. It won't close when the user hovers over the tooltip before the `leaveDelay` is expired.
+
+{{"demo": "pages/demos/tooltips/InteractiveTooltips.js"}}
## Disabled Elements
@@ -52,18 +59,16 @@ By default disabled elements like `Button` do not trigger user interactions so a
{{"demo": "pages/demos/tooltips/DisabledTooltips.js"}}
-## Customized Tooltips
-
-{{"demo": "pages/demos/tooltips/CustomizedTooltips.js"}}
+## Transitions
-## Variable Width
+Use a different transition.
-The `Tooltip` wraps long text by default to make it readable.
+{{"demo": "pages/demos/tooltips/TransitionsTooltips.js"}}
-{{"demo": "pages/demos/tooltips/VariableWidth.js"}}
+## Showing and hiding
-## Interactive
+The tooltip is normally shown immediately when the user's mouse hovers over the element, and hides immediately when the user's mouse leaves. A delay in showing or hiding the tooltip can be added through the properties `enterDelay` and `leaveDelay`, as shown in the Controlled Tooltips demo above.
-A tooltip can be interactive. It won't close when the user hovers over the tooltip before the `leaveDelay` is expired.
+On mobile, the tooltip is displayed when the user longpresses the element and hides after a delay of 1500ms. You can disable this feature with the `disableTouchListener` property.
-{{"demo": "pages/demos/tooltips/InteractiveTooltips.js"}}
+{{"demo": "pages/demos/tooltips/DelayTooltips.js"}}
diff --git a/docs/src/pages/discover-more/team/Team.js b/docs/src/pages/discover-more/team/Team.js
index e7a274ade118b7..07c7c1f77b5990 100644
--- a/docs/src/pages/discover-more/team/Team.js
+++ b/docs/src/pages/discover-more/team/Team.js
@@ -28,16 +28,23 @@ const members = [
name: 'Matt Brookes',
github: 'mbrookes',
twitter: 'randomtechdude',
- flag: 'Documentation wizard 📖',
+ flag: 'Core team',
city: 'London, UK',
},
{
name: 'Kevin Ross',
github: 'rosskevin',
twitter: 'rosskevin',
- flag: 'Core focus',
+ flag: 'Core team',
city: 'Franklin, Tennessee, US',
},
+ {
+ name: 'Sebastian Silbermann',
+ github: 'eps1lon',
+ twitter: 'sebsilbermann',
+ flag: 'Core team',
+ city: 'Dresden, Germany',
+ },
{
name: 'Nathan Marks',
github: 'nathanmarks',
@@ -82,9 +89,7 @@ const members = [
const styles = theme => ({
details: {
- display: 'flex',
- flexDirection: 'column',
- margin: `${theme.spacing.unit}px 0`,
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit}px ${theme.spacing.unit}px 0`,
},
cover: {
width: theme.spacing.unit * 10,
@@ -94,15 +99,9 @@ const styles = theme => ({
flexShrink: 0,
backgroundColor: theme.palette.background.default,
},
- controls: {
- display: 'flex',
- alignItems: 'center',
- },
icon: {
- margin: theme.spacing.unit,
fontSize: 18,
- width: 22,
- height: 22,
+ padding: theme.spacing.unit,
},
});
@@ -113,7 +112,7 @@ function Team(props) {
{members.map(member => (
-
+
{member.city}
-
+
{member.github && (
-
+
)}
{member.twitter && (
@@ -148,10 +147,10 @@ function Team(props) {
href={`https://twitter.com/${member.twitter}`}
className={classes.icon}
>
-
+
)}
-
+
diff --git a/docs/src/pages/getting-started/faq/faq.md b/docs/src/pages/getting-started/faq/faq.md
index 01634eda891bf1..05ef5f41ffeb88 100644
--- a/docs/src/pages/getting-started/faq/faq.md
+++ b/docs/src/pages/getting-started/faq/faq.md
@@ -1,6 +1,6 @@
# Frequently Asked Questions
-Stuck on a particular problem? Check some of these common gotchas first.
+Stuck on a particular problem? Check some of these common gotchas first in our FAQ.
If you still can't find what you're looking for, you can ask the community in [gitter](https://gitter.im/mui-org/material-ui).
For how-to questions and other non-issues, please use [StackOverflow](https://stackoverflow.com/questions/tagged/material-ui) instead of Github issues. There is a StackOverflow tag called `material-ui` that you can use to tag your questions.
@@ -31,12 +31,42 @@ We block the scroll as soon as a modal is opened.
This prevents interacting with the background when the modal should be the only interactive content, however, removing the scrollbar can make your **fixed positioned elements** move.
In this situation, you can apply a global `.mui-fixed` class name to tell Material-UI to handle those elements.
-## How can I disable the ripple effect on the whole app?
+## How can I disable the ripple effect globally?
-The best solution at present is to write wrapping components for all the Material-UI components showing a ripple.
The ripple effect is exclusively coming from the `BaseButton` component.
-You can find the components using the ButtonBase [here](https://github.com/mui-org/material-ui/search?utf8=%E2%9C%93&q=%22%2F%2F+%40inheritedComponent+ButtonBase%22).
-Then, all you have to do is to provide the `disableRipple` property.
+You can disable the ripple effect globally by providing the following in your theme:
+
+```js
+import { createMuiTheme } from '@material-ui/core';
+
+const theme = createMuiTheme({
+ props: {
+ // Name of the component ⚛️
+ MuiButtonBase: {
+ // The properties to apply
+ disableRipple: true, // No more ripple, on the whole application 💣!
+ },
+ },
+});
+```
+
+## How can I disable animations globally?
+
+You can disable animations globally by providing the following in your theme:
+
+```js
+import { createMuiTheme } from '@material-ui/core';
+
+const theme = createMuiTheme({
+ transitions: {
+ // So we have `transition: none;` everywhere
+ create: () => 'none',
+ },
+});
+```
+
+Sometimes you will want to enable this behavior conditionally, for instance during testing or on low-end devices,
+in these cases, you can dynamically change the theme value.
## Do I have to use JSS to style my app?
diff --git a/docs/src/pages/getting-started/installation/installation-zh.md b/docs/src/pages/getting-started/installation/installation-zh.md
index 7ade4d5e693502..cd8e92fe8664d3 100644
--- a/docs/src/pages/getting-started/installation/installation-zh.md
+++ b/docs/src/pages/getting-started/installation/installation-zh.md
@@ -32,7 +32,7 @@ Material-UI的设计考虑了 [Roboto](https://fonts.google.com/specimen/Roboto)
## SVG 图标
-为了使用预构建的SVG Material icons,例如在[组件演示](/demos/app-bar/)中找到的那些, 你必须先安装 [@material-ui/icons](https://www.npmjs.com/package@material-ui/icons)包
+为了使用预构建的SVG Material icons,例如在[组件演示](/demos/app-bar/)中找到的那些, 你必须先安装 [@material-ui/icons](https://www.npmjs.com/package/@material-ui/icons)包
```sh
npm install @material-ui/icons
diff --git a/docs/src/pages/getting-started/installation/installation.md b/docs/src/pages/getting-started/installation/installation.md
index ff7a4b7362a949..26c11f9d63e03a 100644
--- a/docs/src/pages/getting-started/installation/installation.md
+++ b/docs/src/pages/getting-started/installation/installation.md
@@ -23,6 +23,11 @@ For instance, via Google Web Fonts:
```
+Alternatively, if you are using JSX over HTML to render the head:
+```jsx
+
+```
+
## Font Icons
In order to use the font `Icon` component you must first add the [Material icons](https://material.io/tools/icons/) font.
@@ -33,10 +38,15 @@ For instance, via Google Web Fonts:
```
+Alternatively, if you are using JSX over HTML to render the head:
+```jsx
+
+```
+
## SVG Icons
In order to use prebuilt SVG Material icons, such as those found in the [component demos](/demos/app-bar/)
-you must first install the [@material-ui/icons](https://www.npmjs.com/package@material-ui/icons) package:
+you must first install the [@material-ui/icons](https://www.npmjs.com/package/@material-ui/icons) package:
```sh
npm install @material-ui/icons
diff --git a/docs/src/pages/getting-started/page-layout-examples/dashboard/SimpleTable.js b/docs/src/pages/getting-started/page-layout-examples/dashboard/SimpleTable.js
index 1c4f4e58e92a39..2a510d56bde627 100644
--- a/docs/src/pages/getting-started/page-layout-examples/dashboard/SimpleTable.js
+++ b/docs/src/pages/getting-started/page-layout-examples/dashboard/SimpleTable.js
@@ -41,10 +41,10 @@ function SimpleTable(props) {
Dessert (100g serving)
- Calories
- Fat (g)
- Carbs (g)
- Protein (g)
+ Calories
+ Fat (g)
+ Carbs (g)
+ Protein (g)
@@ -54,10 +54,10 @@ function SimpleTable(props) {
{n.name}
- {n.calories}
- {n.fat}
- {n.carbs}
- {n.protein}
+ {n.calories}
+ {n.fat}
+ {n.carbs}
+ {n.protein}
);
})}
diff --git a/docs/src/pages/getting-started/page-layout-examples/page-layout-examples.md b/docs/src/pages/getting-started/page-layout-examples/page-layout-examples.md
index 29674086d014ee..3e48b3c22c47cb 100644
--- a/docs/src/pages/getting-started/page-layout-examples/page-layout-examples.md
+++ b/docs/src/pages/getting-started/page-layout-examples/page-layout-examples.md
@@ -16,3 +16,11 @@ the purpose of each file.
If while using these examples you make changes or enhancements that could improve the
developer experience, or you would like to contribute an additional example,
please consider creating a [pull-request on GitHub](https://github.com/mui-org/material-ui/pulls).
+
+So far we have demos for:
+- A dashboard
+- A sign-in page
+- A blog page
+- A checkout flow
+- An album page
+- A pricing page
diff --git a/docs/src/pages/getting-started/usage/usage.md b/docs/src/pages/getting-started/usage/usage.md
index db0cad44b5afa5..b1d71fea42ded6 100644
--- a/docs/src/pages/getting-started/usage/usage.md
+++ b/docs/src/pages/getting-started/usage/usage.md
@@ -46,7 +46,7 @@ To ensure proper rendering and touch zooming for all devices, add the responsive
+>
```
### CssBaseline
diff --git a/docs/src/pages/guides/migration-v0x/migration-v0x.md b/docs/src/pages/guides/migration-v0x/migration-v0x.md
index e7a835b299698a..c71857609a1d08 100644
--- a/docs/src/pages/guides/migration-v0x/migration-v0x.md
+++ b/docs/src/pages/guides/migration-v0x/migration-v0x.md
@@ -111,6 +111,8 @@ This will apply a change such as the following:
### Raised Button
+RaisedButton upgrade path:
+
```diff
-import RaisedButton from 'material-ui/RaisedButton';
+import Button from '@material-ui/core/Button';
diff --git a/docs/src/pages/layout/grid/grid.md b/docs/src/pages/layout/grid/grid.md
index dccba554df42e3..cf8e09e768ec70 100644
--- a/docs/src/pages/layout/grid/grid.md
+++ b/docs/src/pages/layout/grid/grid.md
@@ -57,6 +57,12 @@ That also means you can set the width of one *item* and the others will automati
{{"demo": "pages/layout/grid/AutoGrid.js"}}
+## Complex Grid
+
+The following demo doesn't follow the Material Design specification, but illustrates how the grid can be used to build complex layouts.
+
+{{"demo": "pages/layout/grid/ComplexGrid.js"}}
+
## CSS Grid Layout
**CSS Grid Layout** excels at dividing a page into major regions, or defining the relationship in terms of size, position, and layer, between parts of a control built from HTML primitives.
@@ -75,12 +81,6 @@ https://www.w3.org/TR/css-flexbox-1/#box-model
{{"demo": "pages/layout/grid/NestedGrid.js"}}
-## Complex Grid
-
-The following demo doesn't follow the Material Design specification, but illustrates how the grid can be used to build complex layouts.
-
-{{"demo": "pages/layout/grid/ComplexGrid.js"}}
-
## Limitations
### Negative margin
@@ -89,7 +89,7 @@ There is one limitation with the negative margin we use to implement the spacing
A horizontal scroll will appear if a negative margin goes beyond the ``.
There are 3 available workarounds:
1. Not using the spacing feature and implementing it in user space `spacing={0}` (default).
-2. Adding a padding on the parent with, at least, the spacing value:
+2. Applying padding to the parent with at least half the the spacing value applied to the child:
```jsx
@@ -99,7 +99,7 @@ There are 3 available workarounds:
```
-3. Adding `overflow-x: hidden;` on the parent.
+3. Adding `overflow-x: hidden;` to the parent.
### white-space: nowrap;
diff --git a/docs/src/pages/premium-themes/PremiumThemes.js b/docs/src/pages/premium-themes/PremiumThemes.js
index dedec060d7d199..07cae7a6cbfe97 100644
--- a/docs/src/pages/premium-themes/PremiumThemes.js
+++ b/docs/src/pages/premium-themes/PremiumThemes.js
@@ -27,7 +27,7 @@ const themes = [
{
name: 'Material Dashboard Pro',
description: 'Material Dashboard Pro React is a Premium Material-UI Admin.',
- src: '/static/images/themes/creative-tim-dashboard.jpg',
+ src: '/static/themes/creative-tim-dashboard.jpg',
price: '$59',
category: 'Admin & Dashboard',
href: 'https://www.creative-tim.com/product/material-dashboard-pro-react?partner=104080',
@@ -35,7 +35,7 @@ const themes = [
{
name: 'Material Kit Pro',
description: 'A Badass Material-UI Kit based on Material Design.',
- src: '/static/images/themes/creative-tim-kit.jpg',
+ src: '/static/themes/creative-tim-kit.jpg',
price: '$89',
category: 'Components',
href: 'https://www.creative-tim.com/product/material-kit-pro-react?partner=104080',
@@ -43,7 +43,7 @@ const themes = [
{
name: 'Material Dashboard',
description: 'Material Dashboard React is a Free Material-UI Admin.',
- src: '/static/images/themes/creative-tim-dashboard.jpg',
+ src: '/static/themes/creative-tim-dashboard.jpg',
price: 'FREE',
category: 'Admin & Dashboard',
href: 'https://www.creative-tim.com/product/material-dashboard-react?partner=104080',
@@ -51,7 +51,7 @@ const themes = [
{
name: 'Material Kit',
description: 'A Badass Material-UI Kit based on Material Design.',
- src: '/static/images/themes/creative-tim-kit.jpg',
+ src: '/static/themes/creative-tim-kit.jpg',
price: 'FREE',
category: 'Components',
href: 'https://www.creative-tim.com/product/material-kit-react?partner=104080',
@@ -61,13 +61,23 @@ const themes = [
description:
'A page that mimics Firebase. ' +
'This item includes theming using the theme provider component.',
- src: '/static/images/themes/paperbase.png',
+ src: '/static/themes/paperbase.png',
price: 'FREE',
category: 'Admin & Dashboard',
href: '/premium-themes/paperbase',
source:
'https://github.com/mui-org/material-ui/tree/master/docs/src/pages/premium-themes/paperbase',
},
+ {
+ name: 'Onepirate',
+ description: 'An example landing and sign-up page.',
+ src: '/static/themes/onepirate.jpg',
+ price: 'FREE',
+ category: 'Landing page',
+ href: '/premium-themes/onepirate',
+ source:
+ 'https://github.com/mui-org/material-ui/tree/master/docs/src/pages/premium-themes/onepirate',
+ },
];
function PremiumThemes(props) {
diff --git a/docs/src/pages/premium-themes/onepirate/ForgotPassword.js b/docs/src/pages/premium-themes/onepirate/ForgotPassword.js
new file mode 100644
index 00000000000000..4b3e6965e738c9
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/ForgotPassword.js
@@ -0,0 +1,121 @@
+import withRoot from './modules/withRoot';
+// --- Post bootstrap -----
+import React from 'react';
+import PropTypes from 'prop-types';
+import compose from 'recompose/compose';
+import { withStyles } from '@material-ui/core/styles';
+import { Field, Form, FormSpy } from 'react-final-form';
+import Typography from './modules/components/Typography';
+import AppFooter from './modules/views/AppFooter';
+import AppAppBar from './modules/views/AppAppBar';
+import AppForm from './modules/views/AppForm';
+import { email, required } from './modules/form/validation';
+import RFTextField from './modules/form/RFTextField';
+import FormButton from './modules/form/FormButton';
+import FormFeedback from './modules/form/FormFeedback';
+
+const styles = theme => ({
+ form: {
+ marginTop: theme.spacing.unit * 6,
+ },
+ button: {
+ marginTop: theme.spacing.unit * 3,
+ marginBottom: theme.spacing.unit * 2,
+ },
+ feedback: {
+ marginTop: theme.spacing.unit * 2,
+ },
+});
+
+class ForgotPassword extends React.Component {
+ state = {
+ sent: false,
+ };
+
+ validate = values => {
+ const errors = required(['email', 'password'], values, this.props);
+
+ if (!errors.email) {
+ const emailError = email(values.email, values, this.props);
+ if (emailError) {
+ errors.email = email(values.email, values, this.props);
+ }
+ }
+
+ return errors;
+ };
+
+ handleSubmit = () => {};
+
+ render() {
+ const { classes } = this.props;
+ const { sent } = this.state;
+
+ return (
+
+
+
+
+
+ Forgot your password?
+
+
+ {"Enter your email address below and we we'll" +
+ 'send you a link to reset your password.'}
+
+
+
+ )}
+
+
+
+
+ );
+ }
+}
+
+ForgotPassword.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default compose(
+ withRoot,
+ withStyles(styles),
+)(ForgotPassword);
diff --git a/docs/src/pages/premium-themes/onepirate/Home.js b/docs/src/pages/premium-themes/onepirate/Home.js
new file mode 100644
index 00000000000000..613f0416e87f8f
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/Home.js
@@ -0,0 +1,28 @@
+import withRoot from './modules/withRoot';
+// --- Post bootstrap -----
+import React from 'react';
+import ProductCategories from './modules/views/ProductCategories';
+import ProductSmokingHero from './modules/views/ProductSmokingHero';
+import AppFooter from './modules/views/AppFooter';
+import ProductHero from './modules/views/ProductHero';
+import ProductValues from './modules/views/ProductValues';
+import ProductHowItWorks from './modules/views/ProductHowItWorks';
+import ProductCTA from './modules/views/ProductCTA';
+import AppAppBar from './modules/views/AppAppBar';
+
+function Index() {
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+}
+
+export default withRoot(Index);
diff --git a/docs/src/pages/premium-themes/onepirate/Privacy.js b/docs/src/pages/premium-themes/onepirate/Privacy.js
new file mode 100644
index 00000000000000..6daf11cfcf682e
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/Privacy.js
@@ -0,0 +1,26 @@
+import withRoot from './modules/withRoot';
+// --- Post bootstrap -----
+import React from 'react';
+import Markdown from './modules/components/Markdown';
+import Typography from './modules/components/Typography';
+import LayoutBody from './modules/components/LayoutBody';
+import AppAppBar from './modules/views/AppAppBar';
+import privacy from './modules/views/privacy.md';
+import AppFooter from './modules/views/AppFooter';
+
+function Privacy() {
+ return (
+
+
+
+
+ Privacy
+
+ {privacy}
+
+
+
+ );
+}
+
+export default withRoot(Privacy);
diff --git a/docs/src/pages/premium-themes/onepirate/SignIn.js b/docs/src/pages/premium-themes/onepirate/SignIn.js
new file mode 100644
index 00000000000000..fc385cf1d21c12
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/SignIn.js
@@ -0,0 +1,148 @@
+import withRoot from './modules/withRoot';
+// --- Post bootstrap -----
+import React from 'react';
+import PropTypes from 'prop-types';
+import compose from 'recompose/compose';
+import { withStyles } from '@material-ui/core/styles';
+import { Field, Form, FormSpy } from 'react-final-form';
+import Typography from './modules/components/Typography';
+import AppFooter from './modules/views/AppFooter';
+import AppAppBar from './modules/views/AppAppBar';
+import Link from './modules/next/Link';
+import AppForm from './modules/views/AppForm';
+import { email, required } from './modules/form/validation';
+import RFTextField from './modules/form/RFTextField';
+import FormButton from './modules/form/FormButton';
+import FormFeedback from './modules/form/FormFeedback';
+
+const styles = theme => ({
+ form: {
+ marginTop: theme.spacing.unit * 6,
+ },
+ button: {
+ marginTop: theme.spacing.unit * 3,
+ marginBottom: theme.spacing.unit * 2,
+ },
+ feedback: {
+ marginTop: theme.spacing.unit * 2,
+ },
+});
+
+class SignIn extends React.Component {
+ state = {
+ sent: false,
+ };
+
+ validate = values => {
+ const errors = required(['email', 'password'], values, this.props);
+
+ if (!errors.email) {
+ const emailError = email(values.email, values, this.props);
+ if (emailError) {
+ errors.email = email(values.email, values, this.props);
+ }
+ }
+
+ return errors;
+ };
+
+ handleSubmit = () => {};
+
+ render() {
+ const { classes } = this.props;
+ const { sent } = this.state;
+
+ return (
+
+
+
+
+
+ Sign In
+
+
+ {'Not a member yet? '}
+
+ Sign Up here
+
+
+
+
+ )}
+
+ (
+
+ )}
+ align="center"
+ >
+ Forgot password?
+
+
+
+
+ );
+ }
+}
+
+SignIn.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default compose(
+ withRoot,
+ withStyles(styles),
+)(SignIn);
diff --git a/docs/src/pages/premium-themes/onepirate/SignUp.js b/docs/src/pages/premium-themes/onepirate/SignUp.js
new file mode 100644
index 00000000000000..7edc3d07e4119a
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/SignUp.js
@@ -0,0 +1,155 @@
+import withRoot from './modules/withRoot';
+// --- Post bootstrap -----
+import React from 'react';
+import PropTypes from 'prop-types';
+import compose from 'recompose/compose';
+import { withStyles } from '@material-ui/core/styles';
+import Grid from '@material-ui/core/Grid';
+import { Field, Form, FormSpy } from 'react-final-form';
+import Typography from './modules/components/Typography';
+import AppFooter from './modules/views/AppFooter';
+import AppAppBar from './modules/views/AppAppBar';
+import Link from './modules/next/Link';
+import AppForm from './modules/views/AppForm';
+import { email, required } from './modules/form/validation';
+import RFTextField from './modules/form/RFTextField';
+import FormButton from './modules/form/FormButton';
+import FormFeedback from './modules/form/FormFeedback';
+
+const styles = theme => ({
+ form: {
+ marginTop: theme.spacing.unit * 6,
+ },
+ button: {
+ marginTop: theme.spacing.unit * 3,
+ marginBottom: theme.spacing.unit * 2,
+ },
+ feedback: {
+ marginTop: theme.spacing.unit * 2,
+ },
+});
+
+class SignUp extends React.Component {
+ state = {
+ sent: false,
+ };
+
+ validate = values => {
+ const errors = required(['firstName', 'lastName', 'email', 'password'], values, this.props);
+
+ if (!errors.email) {
+ const emailError = email(values.email, values, this.props);
+ if (emailError) {
+ errors.email = email(values.email, values, this.props);
+ }
+ }
+
+ return errors;
+ };
+
+ handleSubmit = () => {};
+
+ render() {
+ const { classes } = this.props;
+ const { sent } = this.state;
+
+ return (
+
+
+
+
+
+ Sign Up
+
+
+
+ Already have an account?
+
+
+
+
+ )}
+
+
+
+
+ );
+ }
+}
+
+SignUp.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default compose(
+ withRoot,
+ withStyles(styles),
+)(SignUp);
diff --git a/docs/src/pages/premium-themes/onepirate/Terms.js b/docs/src/pages/premium-themes/onepirate/Terms.js
new file mode 100644
index 00000000000000..14aef70d455c02
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/Terms.js
@@ -0,0 +1,26 @@
+import withRoot from './modules/withRoot';
+// --- Post bootstrap -----
+import React from 'react';
+import Markdown from './modules/components/Markdown';
+import Typography from './modules/components/Typography';
+import LayoutBody from './modules/components/LayoutBody';
+import AppAppBar from './modules/views/AppAppBar';
+import terms from './modules/views/terms.md';
+import AppFooter from './modules/views/AppFooter';
+
+function Terms() {
+ return (
+
+
+
+
+ Terms
+
+ {terms}
+
+
+
+ );
+}
+
+export default withRoot(Terms);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/AppBar.js b/docs/src/pages/premium-themes/onepirate/modules/components/AppBar.js
new file mode 100644
index 00000000000000..bdbd17f82a94da
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/AppBar.js
@@ -0,0 +1,20 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import MuiAppBar from '@material-ui/core/AppBar';
+
+const styles = theme => ({
+ root: {
+ color: theme.palette.common.white,
+ },
+});
+
+function AppBar(props) {
+ return ;
+}
+
+AppBar.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(AppBar);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/Button.js b/docs/src/pages/premium-themes/onepirate/modules/components/Button.js
new file mode 100644
index 00000000000000..436ef1e56ac3b3
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/Button.js
@@ -0,0 +1,31 @@
+import React from 'react';
+import { withStyles } from '@material-ui/core/styles';
+import MuiButton from '@material-ui/core/Button';
+
+const styles = theme => ({
+ root: {
+ borderRadius: 0,
+ fontWeight: theme.typography.fontWeightMedium,
+ fontFamily: theme.typography.fontFamilySecondary,
+ padding: `${theme.spacing.unit * 2 - 1}px ${theme.spacing.unit * 4}px`,
+ fontSize: theme.typography.pxToRem(14),
+ boxShadow: 'none',
+ '&:active, &:focus': {
+ boxShadow: 'none',
+ },
+ },
+ sizeSmall: {
+ padding: `${theme.spacing.unit}px ${theme.spacing.unit * 3}px`,
+ fontSize: theme.typography.pxToRem(13),
+ },
+ sizeLarge: {
+ padding: `${theme.spacing.unit * 3 - 3}px ${theme.spacing.unit * 6}px`,
+ fontSize: theme.typography.pxToRem(16),
+ },
+});
+
+function Button(props) {
+ return ;
+}
+
+export default withStyles(styles)(Button);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/LayoutBody.js b/docs/src/pages/premium-themes/onepirate/modules/components/LayoutBody.js
new file mode 100644
index 00000000000000..ee7ad7d39769e7
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/LayoutBody.js
@@ -0,0 +1,135 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import { capitalize } from '@material-ui/core/utils/helpers';
+
+function round(value) {
+ return Math.round(value * 1e4) / 1e4;
+}
+
+const styles = theme => ({
+ margin: {
+ margin: theme.spacing.unit * 7,
+ },
+ marginBottom: {
+ marginBottom: theme.spacing.unit * 12,
+ },
+ widthSmall: {
+ width: 'auto',
+ marginLeft: theme.spacing.unit * 3,
+ marginRight: theme.spacing.unit * 3,
+ [theme.breakpoints.up(660 + theme.spacing.unit * 6)]: {
+ width: 660,
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ },
+ widthMedium: {
+ width: 'auto',
+ marginLeft: theme.spacing.unit * 3,
+ marginRight: theme.spacing.unit * 3,
+ [theme.breakpoints.up(850 + theme.spacing.unit * 6)]: {
+ width: 850,
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ },
+ widthLarge: {
+ width: 'auto',
+ marginLeft: theme.spacing.unit * 3,
+ marginRight: theme.spacing.unit * 3,
+ [theme.breakpoints.up('md')]: {
+ width: 880,
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ [theme.breakpoints.up(round(880 / 0.7777))]: {
+ width: '77.7777%',
+ },
+ [theme.breakpoints.up(round(1400 / 0.7777))]: {
+ width: 1400,
+ },
+ },
+ widthXlarge: {
+ width: 'auto',
+ marginLeft: theme.spacing.unit * 3,
+ marginRight: theme.spacing.unit * 3,
+ [theme.breakpoints.up('md')]: {
+ width: 900,
+ marginLeft: 'auto',
+ marginRight: 'auto',
+ },
+ [theme.breakpoints.up(round(900 / 0.9))]: {
+ width: '90%',
+ },
+ [theme.breakpoints.up(round(1800 / 0.9))]: {
+ width: 1800,
+ },
+ },
+ widthFull: {
+ width: '100%',
+ },
+ fullHeight: {
+ height: '100%',
+ },
+});
+
+function LayoutBody(props) {
+ const {
+ children,
+ classes,
+ className,
+ component: Component,
+ fullHeight,
+ fullWidth,
+ margin,
+ marginBottom,
+ style,
+ width,
+ ...other
+ } = props;
+
+ return (
+
+ {children}
+
+ );
+}
+
+LayoutBody.propTypes = {
+ children: PropTypes.node,
+ classes: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ fullHeight: PropTypes.bool,
+ fullWidth: PropTypes.bool,
+ margin: PropTypes.bool,
+ marginBottom: PropTypes.bool,
+ style: PropTypes.object,
+ width: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge', 'full']),
+};
+
+LayoutBody.defaultProps = {
+ component: 'div',
+ fullHeight: false,
+ fullWidth: false,
+ margin: false,
+ marginBottom: false,
+ width: 'medium',
+};
+
+export default withStyles(styles)(LayoutBody);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/Markdown.js b/docs/src/pages/premium-themes/onepirate/modules/components/Markdown.js
new file mode 100644
index 00000000000000..5b5e418a72bdd6
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/Markdown.js
@@ -0,0 +1,33 @@
+import React from 'react';
+import ReactMarkdown from 'markdown-to-jsx';
+import { withStyles } from '@material-ui/core/styles';
+import Typography from '@material-ui/core/Typography';
+
+const styles = theme => ({
+ listItem: {
+ marginTop: theme.spacing.unit,
+ },
+});
+
+const options = {
+ overrides: {
+ h1: { component: props => },
+ h2: { component: props => },
+ h3: { component: props => },
+ h4: { component: props => },
+ p: { component: props => },
+ li: {
+ component: withStyles(styles)(({ classes, ...props }) => (
+
+
+
+ )),
+ },
+ },
+};
+
+function Markdown(props) {
+ return ;
+}
+
+export default Markdown;
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/Paper.js b/docs/src/pages/premium-themes/onepirate/modules/components/Paper.js
new file mode 100644
index 00000000000000..cf28b927c74da5
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/Paper.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import MuiPaper from '@material-ui/core/Paper';
+import { capitalize } from '@material-ui/core/utils/helpers';
+import { withStyles } from '@material-ui/core/styles';
+
+const styles = theme => ({
+ backgroundLight: {
+ backgroundColor: theme.palette.secondary.light,
+ },
+ backgroundMain: {
+ backgroundColor: theme.palette.secondary.main,
+ },
+ backgroundDark: {
+ backgroundColor: theme.palette.secondary.dark,
+ },
+ padding: {
+ padding: theme.spacing.unit,
+ },
+});
+
+function Paper(props) {
+ const { background, classes, className, padding, ...other } = props;
+ return (
+
+ );
+}
+
+Paper.propTypes = {
+ background: PropTypes.oneOf(['light', 'main', 'dark']),
+ classes: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ padding: PropTypes.bool,
+};
+
+Paper.defaultProps = {
+ background: 'light',
+ padding: false,
+};
+
+export default withStyles(styles)(Paper);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/Snackbar.js b/docs/src/pages/premium-themes/onepirate/modules/components/Snackbar.js
new file mode 100644
index 00000000000000..b8ad2c2522613c
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/Snackbar.js
@@ -0,0 +1,86 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import MuiSnackbar from '@material-ui/core/Snackbar';
+import Slide from '@material-ui/core/Slide';
+import CloseIcon from '@material-ui/icons/Close';
+import InfoIcon from '@material-ui/icons/Info';
+import IconButton from '@material-ui/core/IconButton';
+
+const styles = theme => ({
+ content: {
+ backgroundColor: theme.palette.secondary.light,
+ color: theme.palette.text.primary,
+ flexWrap: 'inherit',
+ [theme.breakpoints.up('md')]: {
+ borderTopLeftRadius: 0,
+ borderTopRightRadius: 0,
+ borderBottomRightRadius: 4,
+ borderBottomLeftRadius: 4,
+ },
+ },
+ contentMessage: {
+ fontSize: 16,
+ display: 'flex',
+ alignItems: 'center',
+ },
+ contentAction: {
+ paddingLeft: theme.spacing.unit * 2,
+ },
+ info: {
+ flexShrink: 0,
+ marginRight: theme.spacing.unit * 2,
+ },
+ close: {
+ padding: theme.spacing.unit,
+ },
+});
+
+function Transition(props) {
+ return ;
+}
+
+function Snackbar(props) {
+ const { classes, onClose, message, ...other } = props;
+
+ return (
+
+
+ {message}
+
+ }
+ action={[
+
+
+ ,
+ ]}
+ {...other}
+ />
+ );
+}
+
+Snackbar.propTypes = {
+ classes: PropTypes.object.isRequired,
+ SnackbarContentProps: PropTypes.object,
+};
+
+export default withStyles(styles)(Snackbar);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/TextField.js b/docs/src/pages/premium-themes/onepirate/modules/components/TextField.js
new file mode 100644
index 00000000000000..0e57c3d76f1669
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/TextField.js
@@ -0,0 +1,126 @@
+import React from 'react';
+import classNames from 'classnames';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import MuiTextField from '@material-ui/core/TextField';
+import { capitalize } from '@material-ui/core/utils/helpers';
+
+const styles = theme => ({
+ root: {
+ padding: 0,
+ 'label + &': {
+ marginTop: theme.spacing.unit * 3,
+ },
+ },
+ input: {
+ minWidth: theme.spacing.unit * 6,
+ backgroundColor: theme.palette.common.white,
+ '&$disabled': {
+ backgroundColor: theme.palette.divider,
+ },
+ },
+ inputBorder: {
+ border: '1px solid #e9ddd0',
+ '&:focus': {
+ borderColor: theme.palette.secondary.main,
+ },
+ },
+ disabled: {},
+ inputSizeSmall: {
+ fontSize: 14,
+ padding: theme.spacing.unit,
+ width: `calc(100% - ${theme.spacing.unit * 2}px)`,
+ },
+ inputSizeMedium: {
+ fontSize: 16,
+ padding: theme.spacing.unit * 2,
+ width: `calc(100% - ${theme.spacing.unit * 4}px)`,
+ },
+ inputSizeLarge: {
+ fontSize: 18,
+ padding: 22,
+ width: `calc(100% - ${22 * 2}px)`,
+ },
+ inputSizeXlarge: {
+ fontSize: 20,
+ padding: 25,
+ width: `calc(100% - ${25 * 2}px)`,
+ },
+ formLabel: {
+ fontSize: 18,
+ },
+ select: {
+ height: 'auto',
+ borderRadius: 0,
+ },
+ selectIcon: {
+ top: '50%',
+ marginTop: -12,
+ },
+});
+
+function TextField(props) {
+ const {
+ classes,
+ InputProps: {
+ classes: { input: InputPropsClassesInput, ...InputPropsClassesOther } = {},
+ ...InputPropsOther
+ } = {},
+ InputLabelProps,
+ noBorder,
+ size,
+ SelectProps,
+ ...other
+ } = props;
+
+ return (
+
+ );
+}
+
+TextField.propTypes = {
+ classes: PropTypes.object.isRequired,
+ InputLabelProps: PropTypes.object,
+ InputProps: PropTypes.object,
+ noBorder: PropTypes.bool,
+ SelectProps: PropTypes.object,
+ size: PropTypes.oneOf(['small', 'medium', 'large', 'xlarge']),
+};
+
+TextField.defaultProps = {
+ noBorder: false,
+ size: 'medium',
+};
+
+export default withStyles(styles)(TextField);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/Toolbar.js b/docs/src/pages/premium-themes/onepirate/modules/components/Toolbar.js
new file mode 100644
index 00000000000000..80cca0a2206ce9
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/Toolbar.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import { withStyles } from '@material-ui/core/styles';
+import MuiToolbar from '@material-ui/core/Toolbar';
+
+export const styles = theme => ({
+ root: {
+ height: 64,
+ [theme.breakpoints.up('sm')]: {
+ height: 70,
+ },
+ },
+});
+
+function Toolbar(props) {
+ return ;
+}
+
+export default withStyles(styles)(Toolbar);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/components/Typography.js b/docs/src/pages/premium-themes/onepirate/modules/components/Typography.js
new file mode 100644
index 00000000000000..6b604be819428b
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/components/Typography.js
@@ -0,0 +1,72 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { capitalize } from '@material-ui/core/utils/helpers';
+import MuiTypography from '@material-ui/core/Typography';
+
+const styles = theme => ({
+ markedH2Center: {
+ height: 4,
+ width: 73,
+ display: 'block',
+ margin: `${theme.spacing.unit}px auto 0`,
+ backgroundColor: theme.palette.secondary.main,
+ },
+ markedH3Center: {
+ height: 4,
+ width: 55,
+ display: 'block',
+ margin: `${theme.spacing.unit}px auto 0`,
+ backgroundColor: theme.palette.secondary.main,
+ },
+ markedH4Center: {
+ height: 4,
+ width: 55,
+ display: 'block',
+ margin: `${theme.spacing.unit}px auto 0`,
+ backgroundColor: theme.palette.secondary.main,
+ },
+ markedH6Left: {
+ height: 2,
+ width: 28,
+ display: 'block',
+ marginTop: theme.spacing.unit / 2,
+ background: 'currentColor',
+ },
+});
+
+const headlineMapping = {
+ h1: 'h1',
+ h2: 'h1',
+ h3: 'h1',
+ h4: 'h1',
+ h5: 'h3',
+ h6: 'h2',
+ subtitle1: 'h3',
+};
+
+function Typography(props) {
+ const { children, classes, marked, variant, ...other } = props;
+
+ return (
+
+ {children}
+ {marked ? (
+
+ ) : null}
+
+ );
+}
+
+Typography.propTypes = {
+ children: PropTypes.node,
+ classes: PropTypes.object.isRequired,
+ marked: PropTypes.oneOf([false, 'center', 'left']),
+ variant: PropTypes.string,
+};
+
+Typography.defaultProps = {
+ marked: false,
+};
+
+export default withStyles(styles)(Typography);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/FormButton.js b/docs/src/pages/premium-themes/onepirate/modules/form/FormButton.js
new file mode 100644
index 00000000000000..2b9aeab3ef432c
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/FormButton.js
@@ -0,0 +1,16 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import defer from './defer';
+import Button from '../components/Button';
+
+function FormButton(props) {
+ const { disabled, mounted, ...others } = props;
+ return ;
+}
+
+FormButton.propTypes = {
+ disabled: PropTypes.bool,
+ mounted: PropTypes.bool,
+};
+
+export default defer(FormButton);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js b/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js
new file mode 100644
index 00000000000000..0df679db7c6e58
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/FormFeedback.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import Typography from '../components/Typography';
+
+const styles = theme => ({
+ root: {
+ padding: theme.spacing.unit * 2,
+ },
+ error: {
+ backgroundColor: theme.palette.error.xLight,
+ color: theme.palette.error.dark,
+ },
+ success: {
+ backgroundColor: theme.palette.success.xLight,
+ color: theme.palette.success.dark,
+ },
+});
+
+function FormFeedback(props) {
+ return (
+
+ {props.children}
+
+ );
+}
+
+FormFeedback.propTypes = {
+ children: PropTypes.node,
+ classes: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ error: PropTypes.bool,
+ success: PropTypes.bool,
+};
+
+export default withStyles(styles)(FormFeedback);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/RFTextField.js b/docs/src/pages/premium-themes/onepirate/modules/form/RFTextField.js
new file mode 100644
index 00000000000000..c8ed7043800241
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/RFTextField.js
@@ -0,0 +1,40 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import TextField from '../components/TextField';
+
+function RFTextField(props) {
+ const {
+ autoComplete,
+ input,
+ InputProps,
+ meta: { touched, error, submitError },
+ ...other
+ } = props;
+
+ return (
+
+ );
+}
+
+RFTextField.propTypes = {
+ autoComplete: PropTypes.string,
+ input: PropTypes.object.isRequired,
+ InputProps: PropTypes.object,
+ meta: PropTypes.shape({
+ error: PropTypes.string,
+ touched: PropTypes.bool.isRequired,
+ }).isRequired,
+};
+
+export default RFTextField;
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/defer.js b/docs/src/pages/premium-themes/onepirate/modules/form/defer.js
new file mode 100644
index 00000000000000..21307b963e4878
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/defer.js
@@ -0,0 +1,21 @@
+import React from 'react';
+
+function defer(Component) {
+ class Defer extends React.Component {
+ state = {
+ mounted: false,
+ };
+
+ componentDidMount() {
+ this.setState({ mounted: true });
+ }
+
+ render() {
+ return ;
+ }
+ }
+
+ return Defer;
+}
+
+export default defer;
diff --git a/docs/src/pages/premium-themes/onepirate/modules/form/validation.js b/docs/src/pages/premium-themes/onepirate/modules/form/validation.js
new file mode 100644
index 00000000000000..1d4b690fd41a0a
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/form/validation.js
@@ -0,0 +1,18 @@
+import isEmail from 'validator/lib/isEmail';
+
+export function email(value) {
+ return value && !isEmail(value.trim()) ? 'Invalid email' : null;
+}
+
+function isDirty(value) {
+ return value || value === 0;
+}
+
+export function required(requiredFields, values) {
+ return requiredFields.reduce((fields, field) => {
+ return {
+ ...fields,
+ ...(isDirty(values[field]) ? undefined : { [field]: 'Required' }),
+ };
+ }, {});
+}
diff --git a/docs/src/pages/premium-themes/onepirate/modules/next/Link.js b/docs/src/pages/premium-themes/onepirate/modules/next/Link.js
new file mode 100644
index 00000000000000..d830149b290741
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/next/Link.js
@@ -0,0 +1,115 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import compose from 'recompose/compose';
+import NextLink from 'next/link';
+import { withRouter } from 'next/router';
+import { withStyles } from '@material-ui/core/styles';
+
+const styles = theme => ({
+ root: {
+ textDecoration: 'inherit',
+ '&:hover': {
+ textDecoration: 'underline',
+ },
+ },
+ default: {
+ color: 'inherit',
+ },
+ underline: {
+ color: 'inherit',
+ textDecoration: 'underline',
+ },
+ primary: {
+ color: theme.palette.primary.main,
+ },
+ secondary: {
+ color: theme.palette.secondary.main,
+ },
+ button: {
+ '&:hover': {
+ textDecoration: 'inherit',
+ },
+ },
+ active: {},
+});
+
+function Link(props) {
+ const {
+ children: childrenProp,
+ classes,
+ className: classNameProp,
+ component: ComponentProp,
+ href,
+ params,
+ prefetch,
+ router,
+ variant,
+ ...other
+ } = props;
+
+ let Component;
+ const className = classNames(
+ classes.root,
+ {
+ [classes[variant]]: variant !== 'inherit',
+ },
+ classNameProp,
+ );
+ let more;
+ let children = childrenProp;
+
+ if (ComponentProp) {
+ Component = ComponentProp;
+ more = {
+ className,
+ ...other,
+ };
+ } else if (href) {
+ Component = NextLink;
+ more = {
+ href,
+ ...params,
+ prefetch,
+ };
+ children = (
+
+ {children}
+
+ );
+ } else {
+ Component = 'a';
+ more = {
+ ...other,
+ className,
+ };
+ }
+
+ return {children} ;
+}
+
+Link.propTypes = {
+ children: PropTypes.node.isRequired,
+ classes: PropTypes.object.isRequired,
+ className: PropTypes.string,
+ component: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ href: PropTypes.string,
+ params: PropTypes.object,
+ prefetch: PropTypes.bool,
+ router: PropTypes.object.isRequired,
+ variant: PropTypes.oneOf(['default', 'underline', 'primary', 'secondary', 'button', 'inherit']),
+};
+
+Link.defaultProps = {
+ variant: 'primary',
+};
+
+export default compose(
+ withStyles(styles),
+ withRouter,
+)(Link);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/theme.js b/docs/src/pages/premium-themes/onepirate/modules/theme.js
new file mode 100644
index 00000000000000..0dcc8a37f2c9e3
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/theme.js
@@ -0,0 +1,110 @@
+import { createMuiTheme } from '@material-ui/core/styles';
+import green from '@material-ui/core/colors/green';
+import grey from '@material-ui/core/colors/grey';
+import red from '@material-ui/core/colors/red';
+
+const rawTheme = createMuiTheme({
+ palette: {
+ primary: {
+ ligth: '#69696a',
+ main: '#28282a',
+ dark: '#1e1e1f',
+ },
+ secondary: {
+ light: '#fff5f8',
+ main: '#ff3366',
+ dark: '#e62958',
+ },
+ warning: {
+ main: '#ffc071',
+ dark: '#ffb25e',
+ },
+ error: {
+ xLight: red[50],
+ main: red[500],
+ dark: red[700],
+ },
+ success: {
+ xLight: green[50],
+ dark: green[700],
+ },
+ },
+ typography: {
+ fontFamily: "'Work Sans', sans-serif",
+ fontSize: 14,
+ fontWeightLight: 300, // Work Sans
+ fontWeightRegular: 400, // Work Sans
+ fontWeightMedium: 700, // Roboto Condensed
+ fontFamilySecondary: "'Roboto Condensed', sans-serif",
+ useNextVariants: true,
+ },
+});
+
+const fontHeader = {
+ color: rawTheme.palette.text.primary,
+ fontWeight: rawTheme.typography.fontWeightMedium,
+ fontFamily: rawTheme.typography.fontFamilySecondary,
+ textTransform: 'uppercase',
+};
+
+const theme = {
+ ...rawTheme,
+ palette: {
+ ...rawTheme.palette,
+ background: {
+ ...rawTheme.palette.background,
+ default: rawTheme.palette.common.white,
+ placeholder: grey[200],
+ },
+ },
+ typography: {
+ ...rawTheme.typography,
+ fontHeader,
+ h1: {
+ ...rawTheme.typography.h1,
+ ...fontHeader,
+ letterSpacing: 0,
+ fontSize: 60,
+ },
+ h2: {
+ ...rawTheme.typography.h2,
+ ...fontHeader,
+ fontSize: 48,
+ },
+ h3: {
+ ...rawTheme.typography.h3,
+ ...fontHeader,
+ fontSize: 42,
+ },
+ h4: {
+ ...rawTheme.typography.h4,
+ ...fontHeader,
+ fontSize: 36,
+ },
+ h5: {
+ ...rawTheme.typography.h5,
+ fontSize: 20,
+ fontWeight: rawTheme.typography.fontWeightLight,
+ },
+ h6: {
+ ...rawTheme.typography.h6,
+ ...fontHeader,
+ fontSize: 18,
+ },
+ subtitle1: {
+ ...rawTheme.typography.subtitle1,
+ fontSize: 18,
+ },
+ body1: {
+ ...rawTheme.typography.body2,
+ fontWeight: rawTheme.typography.fontWeightRegular,
+ fontSize: 16,
+ },
+ body2: {
+ ...rawTheme.typography.body1,
+ fontSize: 14,
+ },
+ },
+};
+
+export default theme;
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/AppAppBar.js b/docs/src/pages/premium-themes/onepirate/modules/views/AppAppBar.js
new file mode 100644
index 00000000000000..b97bce27867d5b
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/AppAppBar.js
@@ -0,0 +1,89 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import AppBar from '../components/AppBar';
+import Toolbar, { styles as toolbarStyles } from '../components/Toolbar';
+import Typography from '../components/Typography';
+import Link from '../next/Link';
+
+const styles = theme => ({
+ title: {
+ fontSize: 24,
+ },
+ placeholder: toolbarStyles(theme).root,
+ toolbar: {
+ justifyContent: 'space-between',
+ },
+ left: {
+ flex: 1,
+ },
+ leftLinkActive: {
+ color: theme.palette.common.white,
+ },
+ right: {
+ flex: 1,
+ display: 'flex',
+ justifyContent: 'flex-end',
+ },
+ rightLink: {
+ fontSize: 16,
+ color: theme.palette.common.white,
+ marginLeft: theme.spacing.unit * 3,
+ },
+ linkSecondary: {
+ color: theme.palette.secondary.main,
+ },
+});
+
+function AppAppBar(props) {
+ const { classes } = props;
+
+ return (
+
+
+
+
+ (
+
+ )}
+ >
+ {'onepirate'}
+
+
+ (
+
+ )}
+ >
+ {'Sign In'}
+
+ (
+
+ )}
+ >
+ {'Sign Up'}
+
+
+
+
+
+
+ );
+}
+
+AppAppBar.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(AppAppBar);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/AppFooter.js b/docs/src/pages/premium-themes/onepirate/modules/views/AppFooter.js
new file mode 100644
index 00000000000000..6ac35e529fd471
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/AppFooter.js
@@ -0,0 +1,157 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import compose from 'recompose/compose';
+import pure from 'recompose/pure';
+import { withStyles } from '@material-ui/core/styles';
+import Grid from '@material-ui/core/Grid';
+import LayoutBody from '../components/LayoutBody';
+import Typography from '../components/Typography';
+import Link from '../next/Link';
+import TextField from '../components/TextField';
+
+const styles = theme => ({
+ root: {
+ display: 'flex',
+ backgroundColor: theme.palette.secondary.light,
+ },
+ layoutBody: {
+ marginTop: theme.spacing.unit * 8,
+ marginBottom: theme.spacing.unit * 8,
+ display: 'flex',
+ },
+ iconsWrapper: {
+ height: 120,
+ },
+ icons: {
+ display: 'flex',
+ },
+ icon: {
+ width: 48,
+ height: 48,
+ display: 'flex',
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: theme.palette.warning.main,
+ marginRight: theme.spacing.unit,
+ '&:hover': {
+ backgroundColor: theme.palette.warning.dark,
+ },
+ },
+ list: {
+ margin: 0,
+ listStyle: 'none',
+ paddingLeft: 0,
+ },
+ listItem: {
+ paddingTop: theme.spacing.unit / 2,
+ paddingBottom: theme.spacing.unit / 2,
+ },
+ language: {
+ marginTop: theme.spacing.unit,
+ width: 150,
+ },
+});
+
+const LANGUAGES = [
+ {
+ code: 'en-US',
+ name: 'English',
+ },
+ {
+ code: 'fr-FR',
+ name: 'Français',
+ },
+];
+
+function AppFooter(props) {
+ const { classes } = props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ © 2018 Onepirate
+
+
+
+
+ Legal
+
+
+
+ Terms
+
+
+ Privacy
+
+
+
+
+
+ Language
+
+
+ {LANGUAGES.map(language => (
+
+ {language.name}
+
+ ))}
+
+
+
+
+ {'Icons made by '}
+
+ Freepik
+
+ {' from '}
+
+ www.flaticon.com
+
+ {' is licensed by '}
+
+ CC 3.0 BY
+
+
+
+
+
+
+ );
+}
+
+AppFooter.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default compose(
+ pure,
+ withStyles(styles),
+)(AppFooter);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/AppForm.js b/docs/src/pages/premium-themes/onepirate/modules/views/AppForm.js
new file mode 100644
index 00000000000000..88110a61c4c679
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/AppForm.js
@@ -0,0 +1,38 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import LayoutBody from '../components/LayoutBody';
+import Paper from '../components/Paper';
+
+const styles = theme => ({
+ root: {
+ display: 'flex',
+ backgroundImage: 'url(/static/onepirate/appCurvyLines.png)',
+ backgroundRepeat: 'no-repeat',
+ },
+ paper: {
+ padding: `${theme.spacing.unit * 4}px ${theme.spacing.unit * 3}px`,
+ [theme.breakpoints.up('md')]: {
+ padding: `${theme.spacing.unit * 10}px ${theme.spacing.unit * 8}px`,
+ },
+ },
+});
+
+function AppForm(props) {
+ const { children, classes } = props;
+
+ return (
+
+ );
+}
+
+AppForm.propTypes = {
+ children: PropTypes.node.isRequired,
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(AppForm);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductCTA.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductCTA.js
new file mode 100644
index 00000000000000..687a543a78ba05
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductCTA.js
@@ -0,0 +1,131 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Grid from '@material-ui/core/Grid';
+import Hidden from '@material-ui/core/Hidden';
+import Typography from '../components/Typography';
+import TextField from '../components/TextField';
+import Snackbar from '../components/Snackbar';
+import LayoutBody from '../components/LayoutBody';
+import Button from '../components/Button';
+
+const styles = theme => ({
+ root: {
+ marginTop: theme.spacing.unit * 10,
+ marginBottom: 0,
+ display: 'flex',
+ },
+ cardWrapper: {
+ zIndex: 1,
+ },
+ card: {
+ display: 'flex',
+ justifyContent: 'center',
+ backgroundColor: theme.palette.warning.main,
+ padding: `${theme.spacing.unit * 8}px ${theme.spacing.unit * 3}px`,
+ },
+ cardContent: {
+ maxWidth: 400,
+ },
+ textField: {
+ width: '100%',
+ marginTop: theme.spacing.unit * 3,
+ marginBottom: theme.spacing.unit * 2,
+ },
+ button: {
+ width: '100%',
+ },
+ imagesWrapper: {
+ position: 'relative',
+ },
+ imageDots: {
+ position: 'absolute',
+ top: -67,
+ left: -67,
+ right: 0,
+ bottom: 0,
+ width: '100%',
+ background: 'url(/static/onepirate/productCTAImageDots.png)',
+ },
+ image: {
+ position: 'absolute',
+ top: -28,
+ left: -28,
+ right: 0,
+ bottom: 0,
+ width: '100%',
+ maxWidth: 600,
+ },
+});
+
+class ProductCTA extends React.Component {
+ state = {
+ open: false,
+ };
+
+ handleSubmit = event => {
+ event.preventDefault();
+ this.setState({
+ open: true,
+ });
+ };
+
+ handleClose = () => {
+ this.setState({
+ open: false,
+ });
+ };
+
+ render() {
+ const { classes } = this.props;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+}
+
+ProductCTA.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductCTA);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductCategories.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductCategories.js
new file mode 100644
index 00000000000000..026d4878a4f19a
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductCategories.js
@@ -0,0 +1,189 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import ButtonBase from '@material-ui/core/ButtonBase';
+import LayoutBody from '../components/LayoutBody';
+import Typography from '../components/Typography';
+
+const styles = theme => ({
+ root: {
+ marginTop: theme.spacing.unit * 8,
+ marginBottom: theme.spacing.unit * 4,
+ },
+ images: {
+ marginTop: theme.spacing.unit * 8,
+ display: 'flex',
+ flexWrap: 'wrap',
+ },
+ imageWrapper: {
+ position: 'relative',
+ display: 'block',
+ padding: 0,
+ borderRadius: 0,
+ height: '40vh',
+ [theme.breakpoints.down('sm')]: {
+ width: '100% !important',
+ height: 100,
+ },
+ '&:hover': {
+ zIndex: 1,
+ },
+ '&:hover $imageBackdrop': {
+ opacity: 0.15,
+ },
+ '&:hover $imageMarked': {
+ opacity: 0,
+ },
+ '&:hover $imageTitle': {
+ border: '4px solid currentColor',
+ },
+ },
+ imageButton: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ color: theme.palette.common.white,
+ },
+ imageSrc: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ backgroundSize: 'cover',
+ backgroundPosition: 'center 40%',
+ },
+ imageBackdrop: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ background: theme.palette.common.black,
+ opacity: 0.5,
+ transition: theme.transitions.create('opacity'),
+ },
+ imageTitle: {
+ position: 'relative',
+ padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 4}px ${theme.spacing.unit + 6}px`,
+ },
+ imageMarked: {
+ height: 3,
+ width: 18,
+ background: theme.palette.common.white,
+ position: 'absolute',
+ bottom: -2,
+ left: 'calc(50% - 9px)',
+ transition: theme.transitions.create('opacity'),
+ },
+});
+
+function ProductCategories(props) {
+ const { classes } = props;
+
+ const images = [
+ {
+ url:
+ 'https://images.unsplash.com/photo-1534081333815-ae5019106622?auto=format&fit=crop&w=400&q=80',
+ title: 'Snorkeling',
+ width: '40%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1531299204812-e6d44d9a185c?auto=format&fit=crop&w=400&q=80',
+ title: 'Massage',
+ width: '20%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1476480862126-209bfaa8edc8?auto=format&fit=crop&w=400&q=80',
+ title: 'Hiking',
+ width: '40%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1453747063559-36695c8771bd?auto=format&fit=crop&w=400&q=80',
+ title: 'Tour',
+ width: '38%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1523309996740-d5315f9cc28b?auto=format&fit=crop&w=400&q=80',
+ title: 'Gastronomy',
+ width: '38%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1534452203293-494d7ddbf7e0?auto=format&fit=crop&w=400&q=80',
+ title: 'Shopping',
+ width: '24%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1506941433945-99a2aa4bd50a?auto=format&fit=crop&w=400&q=80',
+ title: 'Walking',
+ width: '40%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1533727937480-da3a97967e95?auto=format&fit=crop&w=400&q=80',
+ title: 'Fitness',
+ width: '20%',
+ },
+ {
+ url:
+ 'https://images.unsplash.com/photo-1518136247453-74e7b5265980?auto=format&fit=crop&w=400&q=80',
+ title: 'Reading',
+ width: '40%',
+ },
+ ];
+
+ return (
+
+
+ For all tastes and all desires
+
+
+ {images.map(image => (
+
+
+
+
+
+ ))}
+
+
+ );
+}
+
+ProductCategories.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductCategories);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHero.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHero.js
new file mode 100644
index 00000000000000..03bf7fcc92553f
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHero.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Button from '../components/Button';
+import Typography from '../components/Typography';
+import Link from '../next/Link';
+import ProductHeroLayout from './ProductHeroLayout';
+
+const backgroundImage =
+ 'https://images.unsplash.com/photo-1534854638093-bada1813ca19?auto=format&fit=crop&w=1400&q=80';
+
+const styles = theme => ({
+ background: {
+ backgroundImage: `url(${backgroundImage})`,
+ backgroundColor: '#7fc7d9', // Average color of the background image.
+ backgroundPosition: 'center',
+ },
+ button: {
+ minWidth: 200,
+ },
+ h5: {
+ marginBottom: theme.spacing.unit * 4,
+ marginTop: theme.spacing.unit * 4,
+ [theme.breakpoints.up('sm')]: {
+ marginTop: theme.spacing.unit * 10,
+ },
+ },
+ more: {
+ marginTop: theme.spacing.unit * 2,
+ },
+});
+
+function ProductHero(props) {
+ const { classes } = props;
+
+ return (
+
+ {/* Increase the network loading priority of the background image. */}
+
+
+ Upgrade your Sundays
+
+
+ Enjoy secret offers up to -70% off the best luxury hotels every Sunday.
+
+ (
+
+ )}
+ >
+ Register
+
+
+ Discover the experience
+
+
+ );
+}
+
+ProductHero.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductHero);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js
new file mode 100644
index 00000000000000..a1eff875423788
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHeroLayout.js
@@ -0,0 +1,85 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import classNames from 'classnames';
+import { withStyles } from '@material-ui/core/styles';
+import LayoutBody from '../components/LayoutBody';
+
+const styles = theme => ({
+ root: {
+ color: theme.palette.common.white,
+ position: 'relative',
+ display: 'flex',
+ alignItems: 'center',
+ [theme.breakpoints.up('sm')]: {
+ height: '80vh',
+ minHeight: 500,
+ maxHeight: 1300,
+ },
+ },
+ layoutBody: {
+ marginTop: theme.spacing.unit * 3,
+ marginBottom: theme.spacing.unit * 14,
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ },
+ backdrop: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ backgroundColor: theme.palette.common.black,
+ opacity: 0.5,
+ zIndex: -1,
+ },
+ background: {
+ position: 'absolute',
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: 0,
+ backgroundSize: 'cover',
+ backgroundRepeat: 'no-repeat',
+ zIndex: -2,
+ },
+ arrowDown: {
+ position: 'absolute',
+ bottom: theme.spacing.unit * 4,
+ },
+});
+
+function ProductHeroLayout(props) {
+ const { backgroundClassName, children, classes } = props;
+
+ return (
+
+
+
+ {children}
+
+
+
+
+
+ );
+}
+
+ProductHeroLayout.propTypes = {
+ backgroundClassName: PropTypes.string.isRequired,
+ children: PropTypes.node.isRequired,
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductHeroLayout);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductHowItWorks.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHowItWorks.js
new file mode 100644
index 00000000000000..c6ec977713aad5
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductHowItWorks.js
@@ -0,0 +1,133 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Grid from '@material-ui/core/Grid';
+import LayoutBody from '../components/LayoutBody';
+import Button from '../components/Button';
+import Typography from '../components/Typography';
+import Link from '../next/Link';
+
+const styles = theme => ({
+ root: {
+ display: 'flex',
+ backgroundColor: theme.palette.secondary.light,
+ overflow: 'hidden',
+ },
+ layoutBody: {
+ marginTop: theme.spacing.unit * 10,
+ marginBottom: theme.spacing.unit * 15,
+ position: 'relative',
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ },
+ item: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: `0px ${theme.spacing.unit * 5}px`,
+ },
+ title: {
+ marginBottom: theme.spacing.unit * 14,
+ },
+ number: {
+ fontSize: 24,
+ fontFamily: theme.typography.fontFamily,
+ color: theme.palette.secondary.main,
+ fontWeight: theme.typography.fontWeightMedium,
+ },
+ image: {
+ height: 55,
+ marginTop: theme.spacing.unit * 4,
+ marginBottom: theme.spacing.unit * 4,
+ },
+ curvyLines: {
+ pointerEvents: 'none',
+ position: 'absolute',
+ top: -180,
+ opacity: 0.7,
+ },
+ button: {
+ marginTop: theme.spacing.unit * 8,
+ },
+});
+
+function ProductHowItWorks(props) {
+ const { classes } = props;
+
+ return (
+
+
+
+
+ How it works
+
+
+
+
+
+
1.
+
+
+ Appointment every Wednesday 9am.
+
+
+
+
+
+
2.
+
+
+ First come, first served. Our offers are in limited quantities, so be quick.
+
+
+
+
+
+
3.
+
+
+ {'New offers every week. New experiences, new surprises. '}
+ {'Your Sundays will no longer be alike.'}
+
+
+
+
+
+ (
+
+ )}
+ >
+ Get started
+
+
+
+ );
+}
+
+ProductHowItWorks.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductHowItWorks);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductSmokingHero.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductSmokingHero.js
new file mode 100644
index 00000000000000..60992bb368caf1
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductSmokingHero.js
@@ -0,0 +1,53 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import Button from '@material-ui/core/Button';
+import { withStyles } from '@material-ui/core/styles';
+import LayoutBody from '../components/LayoutBody';
+import Typography from '../components/Typography';
+
+const styles = theme => ({
+ root: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ marginTop: theme.spacing.unit * 9,
+ marginBottom: theme.spacing.unit * 9,
+ },
+ button: {
+ border: '4px solid currentColor',
+ borderRadius: 0,
+ height: 'auto',
+ padding: `${theme.spacing.unit * 2}px ${theme.spacing.unit * 5}px`,
+ },
+ link: {
+ marginTop: theme.spacing.unit * 3,
+ marginBottom: theme.spacing.unit * 3,
+ },
+ buoy: {
+ width: 60,
+ },
+});
+
+function ProductSmokingHero(props) {
+ const { classes } = props;
+
+ return (
+
+
+
+ Got any questions? Need help?
+
+
+
+ We are here to help. Get in touch!
+
+
+
+ );
+}
+
+ProductSmokingHero.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductSmokingHero);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/ProductValues.js b/docs/src/pages/premium-themes/onepirate/modules/views/ProductValues.js
new file mode 100644
index 00000000000000..66ac54e50caa48
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/ProductValues.js
@@ -0,0 +1,110 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Grid from '@material-ui/core/Grid';
+import LayoutBody from '../components/LayoutBody';
+import Typography from '../components/Typography';
+
+const styles = theme => ({
+ root: {
+ display: 'flex',
+ overflow: 'hidden',
+ backgroundColor: theme.palette.secondary.light,
+ },
+ layoutBody: {
+ marginTop: theme.spacing.unit * 15,
+ marginBottom: theme.spacing.unit * 30,
+ display: 'flex',
+ position: 'relative',
+ },
+ item: {
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ padding: `0px ${theme.spacing.unit * 5}px`,
+ },
+ image: {
+ height: 55,
+ },
+ title: {
+ marginTop: theme.spacing.unit * 5,
+ marginBottom: theme.spacing.unit * 5,
+ },
+ curvyLines: {
+ pointerEvents: 'none',
+ position: 'absolute',
+ top: -180,
+ },
+});
+
+function ProductValues(props) {
+ const { classes } = props;
+
+ return (
+
+
+
+
+
+
+
+
+ The best luxury hotels
+
+
+ {'From the latest trendy boutique hotel to the iconic palace with XXL pool'}
+ {', go for a mini-vacation just a few subway stops away from your home.'}
+
+
+
+
+
+
+
+ New experiences
+
+
+ {'Privatize a pool, take a Japanese bath or wake up in 900m2 of garden… '}
+ {'your Sundays will not be alike.'}
+
+
+
+
+
+
+
+ Exclusive rates
+
+
+ {'By registering, you will access specially negotiated rates '}
+ {'that you will not find anywhere else.'}
+
+
+
+
+
+
+ );
+}
+
+ProductValues.propTypes = {
+ classes: PropTypes.object.isRequired,
+};
+
+export default withStyles(styles)(ProductValues);
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md b/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md
new file mode 100644
index 00000000000000..412d0a3ecc40b3
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/privacy.md
@@ -0,0 +1,88 @@
+Last motifided: October 7th, 2018.
+
+Material-UI is committed to protecting and respecting your privacy. This Privacy Policy sets out how we collect and process personal information about you when you visit our website argos-ci.com, when you use our products and services (our “Services”), or when you otherwise do business or make contact with us.
+
+Please read this policy carefully to understand how we handle and treat your personal information.
+
+## What information do we collect?
+
+We may collect and process the following personal information from you:
+
+- **Information you provide to us:** We collect personal information when you voluntarily provide us with such information in the course of using our website or Services. For example, when you register to use our Services, we will collect your name, email address and organization information. We also collect personal information from you when you subscribe to our newsletter, or respond to a survey. If you make an enquiry through our website, or contact us in any other way, we will keep a copy of your communications with us.
+- **Information we collect when you do business with us:** We may process your personal information when you do business with us – for example, as a customer or prospective customer, or as a vendor, supplier, consultant or other third party. For example, we may hold your business contact information and financial account information (if any) and other communications you have with us for the purposes of maintaining our business relations with you.
+- **Information we automatically collect:** We may also collect certain technical information by automatic means when you visit our website, such as IP address, browser type and operating system, referring URLs, your use of our website, and other clickstream data. We collect this information automatically through the use of various technologies, such as cookies.
+- **Personal information where we act as a data processor:** We also process personal information on behalf of our customers in the context of supporting our products and services. Where a customer subscribes to our Services for their website, game or app, they will be the ones who control what event data is collected and stored on our systems. For example, they may ask us to log basic user data (e.g. email address or username), device identifiers, IP addresses, event type, and related source code. In such cases, we are data processors acting in accordance with the instructions of our customers. You will need to refer to the privacy policies of our customers to find out more about how such information is handled by them.
+
+## What do we use your information for?
+
+The personal information we collect from you may be used in one of the following ways:
+
+- To deal with your inquiries and requests
+- To create and administer records about any online account that you register with us
+- To provide you with information and access to resources that you have requested from us
+- To provide you with technical support (your information helps us to better respond to your individual needs)
+- To improve our website (we continually strive to improve our website offerings based on the information and feedback we receive from you), including to improve the navigation and content of our sites
+- For website and system administration and security
+- For general business purposes, including to improve customer service (your information helps us to more effectively respond to your customer service requests and support needs), to help us improve the content and functionality of our Services, to better understand our users, to protect against wrongdoing, to enforce our Terms of Service, and to generally manage our business
+- To process transactions and to provide Services to our customers and end-users
+- For recruitment purposes, where you apply for a job with us
+- To administer a contest, promotion, survey, or other site features
+- To send periodic emails. The email address you provide for order processing, will only be used to send you information and updates pertaining to your order. Where it is in accordance with your marketing preferences, we will send occasional marketing emails about our products and services, which you can unsubscribe from at any time using the link provided in the message.
+
+## How do we protect your information?
+
+We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. We offer the use of a secure server. All supplied sensitive/credit information is transmitted via Secure Socket Layer (SSL) technology and then encrypted into our Payment gateway providers database only to be accessible by those authorized with special access rights to such systems, and are required to keep the information confidential. After a transaction, your private information (credit cards, social security numbers, financials, etc.) will not be stored on our servers.
+
+## Do we use cookies?
+
+Yes. Cookies are small files that a site or its service provider transfers to your computers hard drive through your Web browser (if you allow) that enables the sites or service providers systems to recognize your browser and capture and remember certain information.
+
+We use cookies to understand and save your preferences for future visits, to advertise to you on other sites and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future.
+
+You may refuse to accept cookies by activating the setting on your browser which allows you to refuse the setting of cookies. You can find information on popular browsers and how to adjust your cookie preferences at the following websites:
+
+- [Microsoft Interne Explorer](https://www.microsoft.com/info/cookies.mspx)
+- [Mozilla Firefox](https://support.mozilla.org/en-US/kb/enable-and-disable-cookies-website-preferences)
+- [Google Chrome](https://support.google.com/accounts/answer/61416)
+- [Apple Safari](https://support.apple.com/en-us/HT201265)
+
+However, if you choose to disable cookies, you may be unable to access certain parts of our site. Unless you have adjusted your browser setting so that it will refuse cookies, our system will issue cookies when you log on to our site.
+
+## Do we disclose any information to outside parties?
+
+We will only share your information with third parties in certain circumstances:
+
+- We engage certain trusted third parties to perform functions and provide services to us, including cloud hosting services, off-site backups, email service providers, and customer support providers. We will only share your personal information with third parties to the extent necessary to perform these functions, in accordance with the purposes set out in this Privacy Policy and applicable laws.
+- In the event of a corporate sale, merger, reorganization, dissolution or similar event, your personal information may be sold, disposed of, transferred or otherwise disclosed as part of that transaction.
+- We may also disclose information about you to third parties where we believe it necessary or appropriate under law, for example: (1) to protect or defend our rights, interests or property or that of third parties; (2) to comply with legal process, judicial orders or subpoenas; (3) to respond to requests from public or government authorities, including for national security and law enforcement purposes; (4) to prevent or investigate possible wrongdoing in connection with the Services or to enforce our Terms of Service; (5) to protect the vital interests of our users, customers and other third parties.
+- We may use and share aggregated non-personal information with third parties for marketing, advertising and analytics purposes.
+
+We do not sell or trade your personal information to third parties.
+
+## Third Party Links
+
+Occasionally, at our discretion, we may include or offer third party products or services on our website. If you access other websites using the links provided, the operators of these websites may collect information from you that will be used by them in accordance with their privacy policies. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.
+
+## International Transfers
+
+If you are visiting our website or using our Services from outside the United States (US), please be aware that you are sending personal information to the US where our servers are located. The US may not have data protection laws that are as comprehensive or protective as those in your country of residence; however, our collection, storage and use of your personal information will at all times be in accordance with this Privacy Policy.
+
+## Your Rights
+
+If you are from the EU, you may have the right to access a copy of the personal information we hold about you, or to request the correction, amendment or deletion of such information where it is inaccurate or processed in violation of the Privacy Shield Principles. To make such a request, please contact us at the contact details at the left.
+
+We will consider and respond to your request in accordance with the Privacy Shield Principles and applicable laws.
+
+Furthermore, we commit to giving you an opportunity to opt-out if your personal information is to be disclosed to any other independent third parties, or to be used for a purpose materially different from those that are set out in this Privacy Policy. Where sensitive personal information is involved, we will always obtain your express opt-in consent to do such things. If you otherwise wish to limit the use or disclosure of your personal information, please write to us at the contact details further below.
+
+You can also unsubscribe from our marketing communications at any time by following the instructions or unsubscribe mechanism in the email message itself.
+
+## Data Retention
+
+We may retain your personal information as long as you continue to use the Services, have an account with us or for as long as is necessary to fulfil the purposes outlined in the policy. You can ask to close your account by contacting us at the details below and we will delete your personal information on request.
+
+We may however retain personal information for an additional period as is permitted or required under applicable laws, for legal, tax or regulatory reasons, or for legitimate and lawful business purposes.
+
+## Changes to our Privacy Policy
+
+If we decide to change our privacy policy, we will post those changes on this page, and/or update the Privacy Policy modification date below.
diff --git a/docs/src/pages/premium-themes/onepirate/modules/views/terms.md b/docs/src/pages/premium-themes/onepirate/modules/views/terms.md
new file mode 100644
index 00000000000000..d4c13c079c2a9e
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/views/terms.md
@@ -0,0 +1,149 @@
+Last motifided: October 7th, 2018.
+
+## 1. Services
+
+- 1.1 These Material-UI Terms of Service (these “Terms”) apply to the features and functions provided by Functional Software, Inc. (“Material-UI,” “our,” or “we”) via material-ui.com (the “Site”) (collectively, the “Services”). By accessing or using the Site or the Services, you agree to be bound by these Terms. If you do not agree to these Terms, you are not allowed to use the Site or the Services. The “Effective Date” of these Terms is the date you first use the Site, or access any of the Services.
+
+- 1.2 If you are using the Site or accessing the Services in your capacity as an employee, consultant or agent of a company or other entity, you represent that you are an employee, consultant or agent of that company or entity, and that you have the authority to bind that company or entity to these Terms. For the purpose of these Terms, you (and, if applicable, the company or entity that you represent) will be referred to as “Customer” or “you”.
+
+- 1.3 Material-UI reserves the right to change or modify these Terms, or any of our other policies or guidelines, at any time upon notice to you. We may provide that notice in a variety of ways, including, without limitation, sending you an email, posting a notice on the Site, or posting the revised Terms on the Site and revising the date at the top of these Terms. Any changes or modifications will be effective after we provide notice that these Terms have been modified. You acknowledge that your continued use of the Site or any of the Services following such notice constitutes your acceptance of the modified Terms.
+
+- 1.4 Material-UI reserves the right – at any time, and without notice or liability to you – to modify the Site or the Services, or any part of them, temporarily or permanently. We may modify the Services for a variety of reasons, including, without limitation, for the purpose of providing new features, implementing new protocols, maintaining compatibility with emerging standards, or complying with regulatory requirements.
+
+- 1.5 These Terms form a binding agreement between you and Material-UI. Violation of any of the Terms below will result in the termination of your account(s).
+
+## 2. Privacy
+
+Please see Material-UI’ privacy policy at [https://www.material-ui.com/privacy](https://www.material-ui.com/privacy) for information about how we collect, use, and disclose information about users of the Site and the Services. By using the Site and the Services, you consent to our collection, use, and disclosure of information as set forth in our privacy policy, as we may update that policy from time to time.
+
+## 3. Registration
+
+- 3.1 In order to use many aspects of the Services, you must first complete the Material-UI registration process via the Site. During the registration process, you will be asked to select a package to access the Services (each, a “Plan”), which includes: (a) the period during which you can access the Services (the “Subscription Period”); and (b) the fee you must pay to Material-UI in exchange for your right to access the Services (the “Subscription Fees”). All such information is incorporated into these Terms by reference. We have several different types of paid Plans, as well as a free Plan, for which there are no Subscription Fees. One person or legal entity may not sign up for more than one free Plan.
+
+- 3.2 You agree: (a) to provide accurate, current and complete information about you as part of the registration process (“Registration Data”); (b) to maintain the security of your password(s); (c) to maintain and promptly update your Registration Data, and any other information you provide to Material-UI, and to keep it accurate, current and complete; (d) to accept all risks of unauthorized access to your Registration Data, and any other information you provide to Material-UI, via your account(s) or password(s); (e) that you are responsible for maintaining the security of your account and safeguarding your password(s), and (f) that you will be fully responsible for any activities or transactions that take place using your account(s) or password(s), even if you were not aware of them.
+
+## 4. Access to services
+
+Subject to your continued compliance with these Terms, Material-UI grants you a limited, non-transferable, non-exclusive, revocable right and license to: (i) access and use the Services and its associated documentation, solely for your own internal business purposes, for the Subscription Period for which you have paid the applicable Subscription Fees; and (ii) access and use any data or reports that we provide or make available to you as part of your access and use of the Services (collectively, “Reports”), solely in conjunction with your use of the Services. Reports are considered part of the applicable Services, for the purpose of the license granted above. You understand that Material-UI uses third-party vendors and hosting partners to provide the necessary hardware, software, networking, storage, and related technology required to provide the Services, and you agree that Material-UI is not and will not be liable or responsible for the acts or omissions of such third-party vendors or hosting partners.
+
+## 5. Restrictions
+
+Except as expressly authorized by these Terms, you may not: (a) modify, disclose, alter, translate or create derivative works of the Site or the Services; (b) license, sublicense, resell, distribute, lease, rent, lend, transfer, assign or otherwise dispose of the Services or any Report (or any components thereof); (c) offer any part of the Services (including, without limitation, any Report) on a timeshare or service bureau basis; (c) allow or permit any third party to access or use the Services; (d) use the Site or the Services to store or transmit any viruses, software routines, or other code designed to permit anyone to access in an unauthorized manner, disable, erase or otherwise harm software, hardware, or data, or to perform any other harmful actions; (e) build a competitive product or service, or copy any features or functions of the Site or the Services (including, without limitation, the look-and-feel of the Site or the Services); (f) interfere with or disrupt the integrity or performance of the Site or the Services; (g) disclose to any third party any performance information or analysis relating to the Site or the Services; (h) remove, alter or obscure any proprietary notices in or on the Site or the Services, including copyright notices; (i) use the Site or the Services or any product thereof for any illegal or unauthorized purpose, or in a manner which violates any laws or regulations in your jurisdiction; (j) reverse engineer, decompile, disassemble, or otherwise attempt to discover the source code, object code, or underlying structure, ideas, or algorithms that make up the Services or any software, documentation, or data relating to the Services, except to the limited extent that applicable law prohibits such a restriction; or (k) cause or permit any third party to do any of the foregoing.
+
+## 6. Content
+
+- 6.1 If you publish or upload data, images, code or content, or otherwise make (or allow any third party to make) material available by means of the Site or the Services (collectively, “Content”), you agree that you are entirely responsible for such Content, and for any harm or liability resulting from or arising out of that Content. Your responsibility applies whether or not the Content in question constitutes text, graphics, audio files, video files, computer software, or any other type of content, and whether or not you were the original creator or owner of the Content. You agree that you will be responsible for all Content on your account(s), even if placed there by third parties. By publishing or uploading Content to the Site or the Services, you represent and warrant that:
+
+ - a. the Content does not and will not infringe, violate or misappropriate the Intellectual Property Rights of any third party (where “Intellectual Property Rights” are defined as any patents, copyrights, moral rights, trademarks, trade secrets, or any other form of intellectual property rights recognized in any jurisdiction in the world, including applications and registrations for any of the foregoing);
+
+ - b. you have obtained all rights and permissions necessary to publish and/or use the Content in the manner in which you have published and/or used it;
+
+ - c. Material-UI’s use of the Content for the purpose of providing the Services (including, without limitation, downloading, copying, processing, or creating aggregations of the Content) does not and will not (i) violate any applicable laws or regulations, or (ii) infringe, violate, or misappropriate the Intellectual Property Rights of any third party;
+
+ - d. you have fully complied with any third-party licenses relating to the Content;
+
+ - e. the Content does not contain or install any viruses, worms, malware, Trojan horses or other harmful or destructive code;
+
+ - f. the Content does not and will not include any: (i) “personal health information,” as defined under the Health Insurance Portability and Accountability Act, unless you have entered into a separate agreement with us relating to the processing of such data; (ii) government issued identification numbers, including Social Security numbers, drivers’ license numbers or other state-issued identification numbers; (iii) financial account information, including bank account numbers; (iv) payment card data, including credit card or debit card numbers; or (iv) “sensitive” personal data, as defined under Directive 95/46/EC of the European Parliament (“EU Directive”) and any national laws adopted pursuant to the EU Directive, about residents of Switzerland and any member country of the European Union, including racial or ethnic origin, political opinions, religious beliefs, trade union membership, physical or mental health or condition, sexual life, or the commission or alleged commission any crime or offense;
+
+ - g. the Content is not spam, is not randomly-generated, and does not contain unethical or unwanted commercial content designed to drive traffic to third party sites or boost the search engine rankings of third party sites, or for any other unlawful acts (such as phishing), or for misleading recipients as to the source of the material (such as spoofing);
+
+ - h. the Content does not contain threats or incitement to violence, and does not violate the privacy or publicity rights of any third party;
+
+ - i. the Content is not being advertised via unwanted electronic messages (such as, by way of example, spam links on newsgroups, email lists, other blogs and web sites, and similar unsolicited promotional methods);
+
+ - j. the Content is not named in a manner that misleads (or could mislead) third parties into thinking that you are another person or company (by way of example, your Content’s URL or name should not be confusingly similar to the name of another person or entity); and
+
+ - k. you have, in the case of Content that includes computer code, accurately categorized and/or described the type, nature, uses and effects of the materials, whether requested to do so by the Services or otherwise.
+
+- 6.2 By submitting or uploading Content to the Services, you grant Material-UI a worldwide, royalty-free, and non-exclusive license (i) to use, reproduce, modify, adapt and publish that Content solely for the purpose of providing the Services to you; and (ii) to create aggregations and summaries of the Content or portions thereof and to use, disclose, and distribute such aggregations publicly to any third party in support of our business (both during the period that these Terms are in effect, and thereafter), provided that such aggregations and summaries do not directly or indirectly identify you or your Content. If you delete Content, Material-UI will use reasonable efforts to remove it from the Services. You acknowledge, however, that cached copies or other references to the Content may still be available.
+
+- 6.3 Without limiting any of your representations or warranties with respect to the Content, Material-UI has the right (but not the obligation) to reject or remove any Content, without liability or notice to you, that Material-UI believes, in Material-UI’ sole discretion: (i) violates these Terms or any Material-UI policy, (ii) violates or misappropriates the Intellectual Property Rights of any third party, or (iii) is in any way harmful or objectionable.
+
+## 7. Fee and payment terms; Plan upgrade/downgrade/cancellation; Pricing changes
+
+- 7.1 In exchange for your rights to access the Site and use the Services during the Subscription Period, you agree to pay the applicable Subscription Fees to Material-UI. The Subscription Fees do not include taxes; you will be responsible for, and will promptly pay, all taxes associated with your use of the Site and the Services, other than taxes based on our net income. Subscription Fees are payable in full, in advance, in accordance with your Plan, and are non-refundable and non-creditable. You agree to make all payments in U.S. Dollars.
+
+- 7.2 You can cancel your account(s)/subscription(s) via the process set forth in the “Cancel Subscription” section of your Account Settings on the Site. An email or phone request to cancel your account is not considered cancellation. No refunds will be issued, unless expressly stated otherwise. All of your Content will be deleted from the Services within a reasonable time period from when you cancel your account/subscription. Deleted Content cannot be recovered once your account/subscription is cancelled.
+
+- 7.3 If you upgrade from the free Plan to any paid Plan, we will immediately bill you for the applicable Subscription Fees. There will be no refunds or credits for partial months of service, upgrade/downgrade refunds, or refunds for months unused with an open account.
+
+- 7.4 Downgrading your account(s) may cause the loss of Content, features, or capacity of your account(s). We do not accept any liability for such loss.
+
+- 7.5 Each Subscription Period will automatically renew (and we may automatically invoice you) for additional Subscription Periods of equivalent length, unless and until one party provides written notice to the other at least thirty (30) days prior to the expiration of the then-current Subscription Period that it wishes to terminate the subscription at the end of the then-current Subscription Period. We reserve the right to modify the fees for the Services at any time upon thirty (30) days’ prior notice to you, provided that the modified fees will not apply until the next Subscription Period.
+
+- 7.6 Interest on any late payments will accrue at the rate of 1.5% per month, or the highest rate permitted by law, whichever is lower, from the date the amount is due until the date the amount is paid in full. If you are late in paying us, you also agree that, in addition to our rights to suspend your access to the Services, terminate your account(s), downgrade you to a free Plan, and/or pursue any other rights or remedies available to us at law or in equity, you are responsible to reimburse us for any costs that we incur while attempting to collect such late payments.
+
+## 8. DISCLAIMER
+
+YOU ACKNOWLEDGE THAT THE SITE AND THE SERVICES ARE PROVIDED ON AN “AS IS”, “AS AVAILABLE” BASIS, WITHOUT WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, AND THAT YOUR USE OF THE SITE AND THE SERVICES IS AT YOUR SOLE RISK. ARGOS DOES NOT WARRANT: (I) THAT THE SITE OR THE SERVICES WILL MEET YOUR SPECIFIC REQUIREMENTS, (II) THAT THE SITE OR THE SERVICES WILL BE UNINTERRUPTED, TIMELY, SECURE, OR ERROR-FREE, (III) THAT THE RESULTS THAT MAY BE OBTAINED FROM THE USE OF THE SERVICES WILL BE ACCURATE OR RELIABLE, (IV) THAT THE QUALITY OF ANY PRODUCTS, SERVICES, INFORMATION, OR OTHER MATERIAL THAT YOU PURCHASE OR OBTAIN THROUGH THE SITE OR THE SERVICES WILL MEET YOUR EXPECTATIONS, OR (V) THAT ANY ERRORS IN THE SITE OR THE SERVICES WILL BE CORRECTED. ARGOS SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE, TITLE, AND NON-INFRINGEMENT.
+
+## 9. Indemnification obligations
+
+You agree, at your sole expense, to defend, indemnify and hold Material-UI (and its directors, officers, employees, consultants and agents) harmless from and against any and all actual or threatened suits, actions, proceedings (whether at law or in equity), claims, damages, payments, deficiencies, fines, judgments, settlements, liabilities, losses, costs and expenses (including, without limitation, reasonable attorneys’ fees, costs, penalties, interest and disbursements) arising out of or relating to (i) your Content; (ii) your use of the Site or the Services; (iii) your failure to pay any taxes that you owe under these Terms; and (iv) any other actual or alleged breach of any of your obligations under these Terms (including, among other things, any actual or alleged breach of any of your representations or warranties as set forth herein). You will not settle any such claim in any manner that would require Material-UI to pay money or admit wrongdoing of any kind without our prior written consent, which we may withhold in our sole discretion.
+
+## 10. LIMITATION OF LIABILITY
+
+- 10.1 IN NO EVENT WILL ARGOS’S TOTAL, AGGREGATE LIABILITY TO YOU OR TO ANY THIRD PARTY ARISING OUT OF OR RELATED TO THESE TERMS OR YOUR USE OF (OR INABILITY TO USE) ANY PART OF THE SITE OR THE SERVICES EXCEED THE TOTAL AMOUNT YOU ACTUALLY PAID TO ARGOS IN SUBSCRIPTION FEES FOR THE SERVICES DURING THE TWELVE (12) MONTHS IMMEDIATELY PRIOR TO THE ACCRUAL OF THE FIRST CLAIM. MULTIPLE CLAIMS WILL NOT EXPAND THIS LIMITATION.
+
+- 10.2 IN NO EVENT WILL ARGOS BE LIABLE TO YOU OR TO ANY THIRD PARTY FOR ANY LOSS OF PROFITS, LOSS OF USE, LOSS OF REVENUE, LOSS OF GOODWILL, INTERRUPTION OF BUSINESS, LOSS OF DATA, OR ANY INDIRECT, SPECIAL, INCIDENTAL, EXEMPLARY, PUNITIVE OR CONSEQUENTIAL DAMAGES OF ANY KIND ARISING OUT OF, OR IN CONNECTION WITH THESE TERMS OR YOUR USE (OR INABILITY TO USE) ANY PART OF THE SITE OR THE SERVICES, WHETHER IN CONTRACT, TORT, STRICT LIABILITY OR OTHERWISE, EVEN IF WE HAVE BEEN ADVISED OR ARE OTHERWISE AWARE OF THE POSSIBILITY OF SUCH DAMAGES.
+
+- 10.3 THIS SECTION (LIMITATION OF LIABILITY) WILL BE GIVEN FULL EFFECT EVEN IF ANY REMEDY SPECIFIED IN THESE TERMS IS DEEMED TO HAVE FAILED OF ITS ESSENTIAL PURPOSE.
+
+## 11. Ownership; Reservation of rights
+
+- 11.1 As between the parties: (i) you own all right, title and interest in and to your Content; and (ii) Material-UI owns all right, title and interest in and to the Site and the Services, and all Intellectual Property Rights therein. The look and feel of the Site and the Services, including any custom graphics, button icons, and scripts are also the property of Material-UI, and you may not copy, imitate, or use them, in whole or in part, without Material-UI’ prior written consent. Material-UI reserves all rights not expressly granted to you in these Terms, and Material-UI does not grant any licenses to you or to any other party under these Terms, whether by implication, estoppel or otherwise, except as expressly set forth herein.
+
+- 11.2 You acknowledge that any suggestions, comments, or other feedback that you provide to Material-UI with respect to the Site, the Services, or any other Material-UI product or service (collectively, “Feedback”) will be owned by Material-UI, including all Intellectual Property Rights therein, and will be and become Material-UI’ Confidential Information (as defined below). You acknowledge and agree that Material-UI will be free to use, disclose, reproduce, license, and otherwise distribute and exploit the Feedback as Material-UI sees fit, without obligation or restriction of any kind. At our request and expense, you agree to execute documents or take such further actions as we may reasonably request to help us acquire, perfect, and maintain our rights in the Feedback.
+
+## 12 Term, termination, and effect of termination
+
+- 12.1 These Terms will apply to you starting on the Effective Date, and will continue for as long as you are accessing or using the Site or the Services.
+
+- 12.2 Material-UI, in its sole discretion, has the right to suspend your ability to access the Services, without liability, under the following circumstances: (i) for scheduled or emergency maintenance to the Site or the Services, or any part thereof; (ii) if Material-UI believes that you are using the Site or Services in violation of these Terms or applicable law; (iii) if Material-UI believes that your use of the Site or the Services poses a security risk to us or to any third party; (iv) if required by law enforcement or government agency, or otherwise in order to comply with applicable law or regulation; or (v) if you fail to fulfill your payment obligations hereunder. Material-UI also reserves the right to temporarily or permanently suspend your ability to access the Services, without liability, if Material-UI determines, in its sole discretion, that you are engaging in abusive or excessively frequent use of the Services.
+
+- 12.3 Either of us can terminate these Terms upon notice to the other if the other party breaches any of these Terms and fails to cure the breach within fifteen (15) days of receiving written notice of it from the non-breaching party. We reserve the right to terminate these Terms for cause immediately upon notice to you, and without giving you a cure period, if you breach any of these Terms relating to our intellectual property (including your compliance with the access grant and any restrictions) or our Confidential Information (defined below).
+
+- 12.4 We can terminate any free Plan that you have subscribed to, at any time and for any reason, without notice or liability to you. We can terminate any paid Plan that you have subscribed to, for any reason and without liability, by providing notice to you that we intend to terminate your Plan at the end of the then-current Subscription Period.
+
+- 12.5 When these Terms terminate or expire: (i) you will no longer have the right to use or access the Site or the Services as of the date of termination/expiration; (ii) if you owed us any fees prior to such termination/expiration, you will pay those fees immediately; and (iii) each of us will promptly return to the other (or, if the other party requests it, destroy) all Confidential Information belonging to the other. Sections 1, 2, 4 through 10, 11, and 13 through 15 will survive the termination or expiration of these Terms for any reason.
+
+## 13. Support
+
+- 13.1 If you are subscribed to a paid Plan, Material-UI will provide you with email-based support – just write to our support desk at [support@material-ui.com](mailto:support@material-ui.com). While we work hard to respond to you and resolve your issues quickly, we do not warrant that we will respond within any particular timeframe, or that we will be able to resolve your issue. If you are subscribed to a free Plan, while you are welcome to email us your questions, we encourage you to visit our community forum which can provide valuable information to help answer your questions.
+
+## 14. Confidential information
+
+- 14.1 For the purposes of these Terms, “Confidential Information” means any technical or business information disclosed by one party to the other that: (i) if disclosed in writing, is marked “confidential” or “proprietary” at the time of disclosure; (ii) if disclosed orally, is identified as confidential or proprietary at the time of such disclosure, and is summarized in a writing sent by the disclosing Party to the receiving Party within thirty (30) days of the disclosure. For the purposes of these Terms you agree that the Feedback, any Reports we provide to you, and any non-public elements of the Site or the Services (including, without limitation, the source code of any Material-UI-proprietary software), will be deemed to be Material-UI’s Confidential Information, regardless of whether it is marked as such.
+
+- 14.2 Neither of us will use the other party’s Confidential Information, except as permitted by these Terms. Each of us agrees to maintain in confidence and protect the other party’s Confidential Information using at least the same degree of care as it uses for its own information of a similar nature, but in all events at least a reasonable degree of care. Each of us agrees to take all reasonable precautions to prevent any unauthorized disclosure of the other party’s Confidential Information, including, without limitation, disclosing Confidential Information only to its employees, independent contractors, consultants, and legal and financial advisors (collectively, “Representatives”): (i) with a need to know such information, (ii) who are parties to appropriate agreements sufficient to comply with this Section 13, and (iii) who are informed of the nondisclosure obligations imposed by this Section 13. Each party will be responsible for all acts and omissions of its Representatives. The foregoing obligations will not restrict either party from disclosing Confidential Information of the other party pursuant to the order or requirement of a court, administrative agency, or other governmental body, provided that the party required to make such a disclosure gives reasonable notice to the other party to enable them to contest such order or requirement.
+
+- 14.3 The restrictions set forth in Section 13 will not apply with respect to any Confidential Information that: (i) was or becomes publicly known through no fault of the receiving party; (ii) was rightfully known or becomes rightfully known to the receiving party without confidential or proprietary restriction from a source other than the disclosing party who has a right to disclose it; (iii) is approved by the disclosing party for disclosure without restriction in a written document which is signed by a duly authorized officer of such disclosing party; or (iv) the receiving party independently develops without access to or use of the other party’s Confidential Information.
+
+## 15. Trademarks
+
+You acknowledge and agree that any Material-UI names, trademarks, service marks, logos, trade dress, or other branding included on the Site or as part of the Services (collectively, the “Marks”) are owned by Material-UI and may not be copied, imitated, or used (in whole or in part) without Material-UI’s prior written consent. All other trademarks, names, or logos referenced on the Site or the Services (collectively, “Third-Party Trademarks”) are the property of their respective owners, and the use of such Third-Party Trademarks inure to the benefit of their respective owners. The use of such Third-Party Trademarks is intended to denote interoperability, and does not constitute an affiliation by Material-UI or its licensors with any company or an endorsement or approval by that company of Material-UI, its licensors, or their respective products or services.
+
+## 16. General provisions
+
+- 16.1 These Terms, together with any policies incorporated into these Terms by reference, are the complete and exclusive understanding of the parties with respect to Material-UI’s provision of, and your use of and access to, the Site and the Services, and supersede all previous or contemporaneous agreements or communications, whether written or oral, relating to the subject matter of these Terms (including, without limitation, prior versions of these Terms). Any terms or conditions that you send to Material-UI that are inconsistent with or in addition to these Terms are hereby rejected by Material-UI, and will be deemed void and of no effect.
+
+- 16.2 These Terms will be governed by and construed in accordance with the laws of the State of California, without regard to that State’s conflict of law principles. Any legal action or proceeding arising under, related to or connected with these Terms will be brought exclusively in the federal (if they have jurisdiction) or state courts located in San Francisco, California, and the parties irrevocably consent to the personal jurisdiction and venue of such court(s). The United Nations Convention on Contracts for the International Sale of Goods and the Uniform Computer Information Transactions Act will not apply to these Terms. If a party initiates any proceeding regarding these Terms, the prevailing party to such proceeding is entitled to reasonable attorneys’ fees and costs.
+
+- 16.3 You agree that Material-UI has the right to use your name and logo on the Site or other Material-UI websites or marketing materials, for the purposes of identifying you as a Material-UI customer and describing your use of the Services. You also agree that Material-UI may (but is under no obligation to): (i) issue a press release identifying you as a Material-UI customer; (ii) inform other potential customers that you are a user of the Services; and (iii) identify you as a customer in other forms of publicity (including, without limitation, case studies, blog posts, and the like.
+
+- 16.4 You may not assign these Terms, in whole or in part, by operation of law or otherwise, without the prior written consent of Material-UI, and any attempted transfer, assignment or delegation without such consent will be void and of no effect. Material-UI may freely transfer, assign or delegate these Terms, or its rights and duties under these Terms, without notice to you. Subject to the foregoing, these Terms will be binding upon and will inure to the benefit of the parties and their respective representatives, heirs, administrators, successors and permitted assigns.
+
+- 16.5 Except as expressly set forth in these Terms, the exercise by either party of any of its remedies will be without prejudice to its other remedies under these Terms or otherwise. The failure by a party to enforce any part of these Terms will not constitute a waiver of future enforcement of that or any other provision. Any waiver of any provision of these Terms will be effective only if in writing and signed by an authorized representative of the waiving party.
+
+- 16.6 You agree that any notice that Material-UI is required to provide pursuant to these Terms can be given electronically, which may include an email to the email address you provide to Material-UI as part of your Registration Data. These notices can be about a wide variety of things, including responding to your questions, requests for additional information, and legal notices. You agree that such electronic notices satisfy any legal requirement that such communications be in writing. An electronic notice will be deemed to have been received on the day the email is sent to you, provided that the email is the same as the email address you provided as part of your Registration Data.
+
+- 16.7 You acknowledge that you are responsible for complying with all applicable laws and regulations associated with your access and use of the Site and Services, including, without limitation, all applicable export control laws and regulations.
+
+- 16.8 We do not develop any technical data or computer software pursuant to these Terms. The Site and the Services have been developed solely with private funds, are considered “Commercial Computer Software” and “Commercial Computer Software Documentation” as described in FAR 12.212, FAR 27.405-3, and DFARS 227.7202-3, and access is provided to U.S. Government end users as restricted computer software and limited rights data. Any use, disclosure, modification, distribution, or reproduction of the Site or the Services by the U.S. Government, its end users or contractors is subject to the restrictions set forth in these Terms.
+
+- 16.9 If any portion of these Terms is held to be unenforceable or invalid, that portion will be enforced to the maximum extent possible, and all other provisions will remain in full force and effect.
+
+- 16.10 Except for payments due under these Terms, neither party will be responsible for any delay or failure to perform that is attributable in whole or in part to any cause beyond its reasonable control, including, without limitation, acts of God (fire, storm, floods, earthquakes, etc.); civil disturbances; disruption of telecommunications, power or other essential services; interruption or termination of service by any service providers used by Material-UI to host the Services or to link its servers to the Internet; labor disturbances; vandalism; cable cut; computer viruses or other similar occurrences; or any malicious or unlawful acts of any third party.
+
+- 16.11 We are each independent contractors with respect to the subject matter of these Terms. Nothing contained in these Terms will be deemed or construed in any manner whatsoever to create a partnership, joint venture, employment, agency, fiduciary, or other similar relationship between us, and neither of us can bind the other contractually.
diff --git a/docs/src/pages/premium-themes/onepirate/modules/withRoot.js b/docs/src/pages/premium-themes/onepirate/modules/withRoot.js
new file mode 100644
index 00000000000000..5b71e5b24e2a0e
--- /dev/null
+++ b/docs/src/pages/premium-themes/onepirate/modules/withRoot.js
@@ -0,0 +1,21 @@
+import React from 'react';
+import { MuiThemeProvider } from '@material-ui/core/styles';
+import CssBaseline from '@material-ui/core/CssBaseline';
+import theme from './theme';
+
+function withRoot(Component) {
+ function WithRoot(props) {
+ // MuiThemeProvider makes the theme available down the React tree thanks to React context.
+ return (
+
+ {/* CssBaseline kickstart an elegant, consistent, and simple baseline to build upon. */}
+
+
+
+ );
+ }
+
+ return WithRoot;
+}
+
+export default withRoot;
diff --git a/docs/src/pages/style/color/ColorTool.js b/docs/src/pages/style/color/ColorTool.js
index 4fa8c2576b193d..1e80e5c4ea50f1 100644
--- a/docs/src/pages/style/color/ColorTool.js
+++ b/docs/src/pages/style/color/ColorTool.js
@@ -14,7 +14,7 @@ import CheckIcon from '@material-ui/icons/Check';
import Slider from '@material-ui/lab/Slider';
import { rgbToHex } from '@material-ui/core/styles/colorManipulator';
import { capitalize } from '@material-ui/core/utils/helpers';
-import actionTypes from 'docs/src/modules/redux/actionTypes';
+import { ACTION_TYPES } from 'docs/src/modules/constants';
import ColorDemo from './ColorDemo';
import themeInitialState from 'docs/src/modules/styles/themeInitialState';
@@ -131,7 +131,7 @@ class ColorTool extends React.Component {
};
this.props.dispatch({
- type: actionTypes.THEME_CHANGE_PALETTE_COLORS,
+ type: ACTION_TYPES.THEME_CHANGE,
payload: { paletteColors },
});
@@ -145,7 +145,7 @@ class ColorTool extends React.Component {
};
this.props.dispatch({
- type: actionTypes.THEME_CHANGE_PALETTE_COLORS,
+ type: ACTION_TYPES.THEME_CHANGE,
payload: { paletteColors },
});
diff --git a/docs/src/pages/utils/modal/modal.md b/docs/src/pages/utils/modal/modal.md
index 0ac0ed75cc646e..10680ff5cf789e 100644
--- a/docs/src/pages/utils/modal/modal.md
+++ b/docs/src/pages/utils/modal/modal.md
@@ -12,15 +12,59 @@ The component renders its `children` node in front of a backdrop component.
The `Modal` offers a few helpful features over using just a [`Portal`](/utils/portal/)
component and some styles:
-- Manages dialog stacking when one-at-a-time just isn't enough.
+- Manages modal stacking when one-at-a-time just isn't enough.
- Creates a backdrop, for disabling interaction below the modal.
- It properly manages focus; moving to the modal content,
and keeping it there until the modal is closed.
- It disables scrolling of the page content while open.
-- Adds the appropriate ARIA roles automatically.
+- ♿️ Adds the appropriate ARIA roles automatically.
This component shares many concepts with [react-overlays](https://react-bootstrap.github.io/react-overlays/#modals).
## Simple modal
{{"demo": "pages/utils/modal/SimpleModal.js"}}
+
+## Performance
+
+The content of the modal is **lazily mounted** into the DOM.
+It ensures that having many closed modal in your React tree won't slow down your page.
+
+However, creating React elements has a cost too. Consider the following case:
+
+```jsx
+
+
+
+
+ Dessert (100g serving)
+ Calories
+ Fat (g)
+
+
+
+ {rows.map(row => (
+
+
+ {row.name}
+
+ {row.calories}
+ {row.fat}
+
+ ))}
+
+
+
+```
+
+We create a lot of React elements that will never be mounted. It's wasteful 🐢.
+You can **speed up** the rendering by moving the modal body into its own component.
+
+```jsx
+
+
+
+```
+
+This way, you take advantage of React render laziness evaluation.
+The `TableComponent` render method will only be evaluated when opening the modal 🚀.
diff --git a/docs/src/pages/utils/popover/MouseOverPopover.js b/docs/src/pages/utils/popover/MouseOverPopover.js
index a2ca7b3124baeb..03fafedecaf808 100644
--- a/docs/src/pages/utils/popover/MouseOverPopover.js
+++ b/docs/src/pages/utils/popover/MouseOverPopover.js
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/mouse-events-have-key-events */
-
import React from 'react';
import PropTypes from 'prop-types';
import Popover from '@material-ui/core/Popover';
diff --git a/docs/src/pages/utils/popper/popper.md b/docs/src/pages/utils/popper/popper.md
index 33fb4ecbb5db78..e50e98565c9cc4 100644
--- a/docs/src/pages/utils/popper/popper.md
+++ b/docs/src/pages/utils/popper/popper.md
@@ -7,9 +7,11 @@ components: Popper
A Popper can be used to display some content on top of another. It's an alternative to react-popper.
-Things to know when using the `Popper` component:
+Some important features of the `Popper` component:
-- Poppers rely on the 3rd party library [Popper.js](https://github.com/FezVrasta/popper.js) for positioning.
+- 🕷 Popper relies on the 3rd party library ([Popper.js](https://github.com/FezVrasta/popper.js)) for perfect positioning.
+- 💄 It's an alternative API to react-popper. It aims for simplicity.
+- 📦 Less than [10 KB gzipped](https://github.com/mui-org/material-ui/blob/master/.size-limit.js).
- The children is [`Portal`](/utils/portal/) to the body of the document to avoid rendering problems.
You can disable this behavior with `disablePortal`.
- The scroll and click away aren't blocked like with the [`Popover`](/utils/popover/) component.
diff --git a/docs/src/pages/utils/transitions/SimpleGrow.js b/docs/src/pages/utils/transitions/SimpleGrow.js
index c1cde84522aec5..b04372ba9fbe61 100644
--- a/docs/src/pages/utils/transitions/SimpleGrow.js
+++ b/docs/src/pages/utils/transitions/SimpleGrow.js
@@ -39,27 +39,26 @@ class SimpleGrow extends React.Component {
const { classes } = this.props;
const { checked } = this.state;
+ const polygon = (
+
+
+
+
+
+ );
+
return (
-
-
-
-
-
-
-
+
{polygon}
+ {/* Conditionally applies the timeout property to change the entry speed. */}
-
-
-
-
-
+ {polygon}
diff --git a/docs/src/pages/versions/StableVersions.js b/docs/src/pages/versions/StableVersions.js
index a989071877249f..7e63a8d242f150 100644
--- a/docs/src/pages/versions/StableVersions.js
+++ b/docs/src/pages/versions/StableVersions.js
@@ -1,5 +1,3 @@
-/* eslint-disable react/no-did-mount-set-state */
-
import 'isomorphic-fetch';
import React from 'react';
import PropTypes from 'prop-types';
diff --git a/examples/gatsby/gatsby-browser.js b/examples/gatsby/gatsby-browser.js
index 2acdc2e3432b36..093ab8d1934c9a 100644
--- a/examples/gatsby/gatsby-browser.js
+++ b/examples/gatsby/gatsby-browser.js
@@ -1,5 +1,3 @@
-/* eslint-disable react/prop-types, import/prefer-default-export */
-
// It's not ready yet: https://github.com/gatsbyjs/gatsby/issues/8237.
//
// import React from 'react';
diff --git a/examples/gatsby/gatsby-ssr.js b/examples/gatsby/gatsby-ssr.js
index 1069c950d42a4d..5e5daff739a4bd 100644
--- a/examples/gatsby/gatsby-ssr.js
+++ b/examples/gatsby/gatsby-ssr.js
@@ -1,4 +1,4 @@
-/* eslint-disable react/prop-types, react/no-danger */
+/* eslint-disable react/no-danger */
const React = require('react');
const { renderToString } = require('react-dom/server');
diff --git a/examples/gatsby/src/pages/about.js b/examples/gatsby/src/pages/about.js
index 78faee418aa761..3ba75a867ab63d 100644
--- a/examples/gatsby/src/pages/about.js
+++ b/examples/gatsby/src/pages/about.js
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/anchor-is-valid */
-
import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
diff --git a/examples/gatsby/src/pages/index.js b/examples/gatsby/src/pages/index.js
index 4c0bf96922c474..3ac59a5f63f098 100644
--- a/examples/gatsby/src/pages/index.js
+++ b/examples/gatsby/src/pages/index.js
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/anchor-is-valid */
-
import React from 'react';
import PropTypes from 'prop-types';
import Button from '@material-ui/core/Button';
diff --git a/next.config.js b/next.config.js
index f066949fc10087..5796a216ea14a5 100644
--- a/next.config.js
+++ b/next.config.js
@@ -1,12 +1,18 @@
const webpack = require('webpack');
const pkg = require('./package.json');
+const withTM = require('@weco/next-plugin-transpile-modules');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const { findPages } = require('./docs/src/modules/utils/find');
process.env.LIB_VERSION = pkg.version;
module.exports = {
- webpack: config => {
+ webpack: (config, options) => {
+ // Alias @material-ui/core peer dependency imports form the following modules to our sources.
+ config = withTM({
+ transpileModules: ['notistack'],
+ }).webpack(config, options);
+
const plugins = config.plugins.concat([
new webpack.DefinePlugin({
'process.env': {
diff --git a/package.json b/package.json
index 5cb59501b5e06c..d132dc57bc27ce 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "@material-ui/workspace",
"private": true,
"author": "Material-UI Team",
- "version": "3.5.1",
+ "version": "3.6.2",
"description": "Material-UI's workspace package",
"keywords": [
"react",
@@ -34,7 +34,7 @@
"docs:deploy": "git push material-ui-docs master:latest",
"jsonlint": "yarn --silent jsonlint:files | xargs -n1 jsonlint -q -c && echo \"jsonlint: no lint errors\"",
"jsonlint:files": "find . -name \"*.json\" | grep -v -f .eslintignore",
- "lint": "eslint . --cache && echo \"eslint: no lint errors\"",
+ "lint": "eslint . --cache --report-unused-disable-directives && echo \"eslint: no lint errors\"",
"lint:fix": "eslint . --cache --fix && echo \"eslint: no lint errors\"",
"prettier": "yarn babel-node ./scripts/prettier.js",
"prettier:all": "yarn babel-node ./scripts/prettier.js write",
@@ -67,7 +67,8 @@
"@babel/preset-react": "7.0.0",
"@babel/register": "7.0.0",
"@types/enzyme": "^3.1.4",
- "@types/react": "^16.3.14",
+ "@types/react": "^16.7.10",
+ "@weco/next-plugin-transpile-modules": "0.0.2",
"accept-language": "^3.0.18",
"argos-cli": "^0.0.9",
"autoprefixer": "^9.0.0",
@@ -80,12 +81,12 @@
"babel-plugin-react-remove-properties": "^0.2.5",
"babel-plugin-transform-dev-warning": "^0.1.0",
"babel-plugin-transform-react-constant-elements": "^6.23.0",
- "babel-plugin-transform-react-remove-prop-types": "^0.4.10",
+ "babel-plugin-transform-react-remove-prop-types": "^0.4.21",
"chai": "^4.1.2",
"clean-css": "^4.1.11",
"clipboard-copy": "^2.0.0",
"cross-env": "^5.1.1",
- "css-loader": "^1.0.0",
+ "css-loader": "^2.0.0",
"doctrine": "^3.0.0",
"downshift": "^3.0.0",
"dtslint": "^0.3.0",
@@ -102,6 +103,7 @@
"eslint-plugin-react": "^7.4.0",
"fg-loadcss": "^2.0.1",
"file-loader": "^2.0.0",
+ "final-form": "^4.11.0",
"fs-extra": "^7.0.0",
"glob": "^7.1.2",
"glob-gitignore": "^1.0.11",
@@ -123,6 +125,7 @@
"material-ui-popup-state": "^1.0.1",
"mocha": "^5.0.0",
"next": "^7.0.0",
+ "notistack": "^0.4.0",
"nyc": "^13.0.0",
"postcss": "^7.0.0",
"prettier": "^1.8.2",
@@ -132,17 +135,20 @@
"react-autosuggest": "^9.3.2",
"react-docgen": "^3.0.0-beta10",
"react-dom": "^16.7.0-alpha.0",
+ "react-draggable": "^3.0.5",
+ "react-final-form": "^4.0.2",
"react-frame-component": "^4.0.2",
"react-inspector": "^2.2.2",
"react-jss": "^8.1.0",
"react-number-format": "^4.0.0",
- "react-redux": "^5.0.6",
+ "react-redux": "^6.0.0",
"react-router": "^4.2.0",
"react-router-dom": "^4.2.2",
"react-select": "^2.0.0",
"react-swipeable-views": "^0.13.0",
"react-test-renderer": "^16.1.1",
"react-text-mask": "^5.0.2",
+ "react-virtualized": "^9.21.0",
"recast": "^0.16.0",
"recharts": "^1.1.0",
"redux": "^4.0.0",
@@ -167,6 +173,9 @@
"webpack-cli": "^3.1.0",
"workbox-build": "^3.6.3"
},
+ "resolutions": {
+ "**/hoist-non-react-statics": "^3.2.1"
+ },
"sideEffects": false,
"nyc": {
"include": [
diff --git a/packages/material-ui-docs/package.json b/packages/material-ui-docs/package.json
index 6834c70df2504d..886e8bbf58b456 100644
--- a/packages/material-ui-docs/package.json
+++ b/packages/material-ui-docs/package.json
@@ -2,7 +2,7 @@
"name": "@material-ui/docs",
"private": false,
"author": "Material-UI Team",
- "version": "3.0.0-alpha.7",
+ "version": "3.0.0-alpha.8",
"description": "Material-UI Docs - Documentation building blocks.",
"main": "./src/index.js",
"keywords": [
diff --git a/packages/material-ui-docs/src/MarkdownElement/MarkdownElement.js b/packages/material-ui-docs/src/MarkdownElement/MarkdownElement.js
index 9c47bfb03e0100..c85a73fef5497d 100644
--- a/packages/material-ui-docs/src/MarkdownElement/MarkdownElement.js
+++ b/packages/material-ui-docs/src/MarkdownElement/MarkdownElement.js
@@ -22,7 +22,8 @@ export function textToHash(text) {
return text
.toLowerCase()
.replace(/=>|<| \/>||<\/code>/g, '')
- .replace(/\W/g, '-');
+ .replace(/\W+/g, '-')
+ .replace(/-$/g, '');
}
renderer.heading = (text, level) => {
diff --git a/packages/material-ui-docs/src/NProgressBar/NProgressBar.js b/packages/material-ui-docs/src/NProgressBar/NProgressBar.js
index 1be78b9e0b7dda..d0ba9df5390796 100644
--- a/packages/material-ui-docs/src/NProgressBar/NProgressBar.js
+++ b/packages/material-ui-docs/src/NProgressBar/NProgressBar.js
@@ -97,7 +97,9 @@ NProgressBar.propTypes = {
children: PropTypes.node,
};
-NProgressBar.propTypes = exactProp(NProgressBar.propTypes, 'NProgressBar');
+if (process.env.NODE_ENV !== 'production') {
+ NProgressBar.propTypes = exactProp(NProgressBar.propTypes);
+}
NProgressBar.defaultProps = {
children: null,
diff --git a/packages/material-ui-docs/src/svgIcons/HookLogo.js b/packages/material-ui-docs/src/svgIcons/HookLogo.js
new file mode 100644
index 00000000000000..1a422e4d9974b9
--- /dev/null
+++ b/packages/material-ui-docs/src/svgIcons/HookLogo.js
@@ -0,0 +1,19 @@
+/* eslint-disable max-len */
+
+import React from 'react';
+import SvgIcon from '@material-ui/core/SvgIcon';
+
+function HookLogo(props) {
+ return (
+
+
+
+
+
+
+ );
+}
+
+HookLogo.muiName = 'SvgIcon';
+
+export default HookLogo;
diff --git a/packages/material-ui-docs/src/svgIcons/JSLogo.js b/packages/material-ui-docs/src/svgIcons/JSLogo.js
new file mode 100644
index 00000000000000..afa99d7e1ee9a9
--- /dev/null
+++ b/packages/material-ui-docs/src/svgIcons/JSLogo.js
@@ -0,0 +1,19 @@
+/* eslint-disable max-len */
+
+import React from 'react';
+import SvgIcon from '@material-ui/core/SvgIcon';
+
+function JSLogo(props) {
+ return (
+
+
+
+
+
+
+ );
+}
+
+JSLogo.muiName = 'SvgIcon';
+
+export default JSLogo;
diff --git a/packages/material-ui-lab/package.json b/packages/material-ui-lab/package.json
index a34c9d547cecff..e4cee25ccf7c6c 100644
--- a/packages/material-ui-lab/package.json
+++ b/packages/material-ui-lab/package.json
@@ -2,7 +2,7 @@
"name": "@material-ui/lab",
"private": false,
"author": "Material-UI Team",
- "version": "3.0.0-alpha.23",
+ "version": "3.0.0-alpha.25",
"description": "Material-UI Lab - Incubator for Material-UI React components.",
"main": "./src/index.js",
"keywords": [
@@ -38,6 +38,7 @@
},
"dependencies": {
"@babel/runtime": "7.1.2",
+ "@material-ui/utils": "^3.0.0-alpha.0",
"classnames": "^2.2.5",
"keycode": "^2.1.9"
},
diff --git a/packages/material-ui-lab/src/Slider/Slider.d.ts b/packages/material-ui-lab/src/Slider/Slider.d.ts
index 9cda01700fa3b4..957f915a25cf58 100644
--- a/packages/material-ui-lab/src/Slider/Slider.d.ts
+++ b/packages/material-ui-lab/src/Slider/Slider.d.ts
@@ -25,6 +25,8 @@ export type SliderClassKey =
| 'trackBefore'
| 'trackAfter'
| 'thumb'
+ | 'thumbIconWrapper'
+ | 'thumbIcon'
| 'focused'
| 'activated'
| 'disabled'
diff --git a/packages/material-ui-lab/src/Slider/Slider.js b/packages/material-ui-lab/src/Slider/Slider.js
index af8aa852f21711..358f005ddab773 100644
--- a/packages/material-ui-lab/src/Slider/Slider.js
+++ b/packages/material-ui-lab/src/Slider/Slider.js
@@ -6,6 +6,7 @@ import classNames from 'classnames';
import withStyles from '@material-ui/core/styles/withStyles';
import ButtonBase from '@material-ui/core/ButtonBase';
import { fade } from '@material-ui/core/styles/colorManipulator';
+import { componentPropType } from '@material-ui/utils';
import clamp from '../utils/clamp';
export const styles = theme => {
@@ -557,7 +558,7 @@ Slider.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the slider will be disabled.
*/
diff --git a/packages/material-ui-lab/src/SpeedDial/SpeedDial.js b/packages/material-ui-lab/src/SpeedDial/SpeedDial.js
index 0f7a127cf216f9..a3aeed08bc04a8 100644
--- a/packages/material-ui-lab/src/SpeedDial/SpeedDial.js
+++ b/packages/material-ui-lab/src/SpeedDial/SpeedDial.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import keycode from 'keycode';
import warning from 'warning';
+import { componentPropType } from '@material-ui/utils';
import { withStyles } from '@material-ui/core/styles';
import Zoom from '@material-ui/core/Zoom';
import { duration } from '@material-ui/core/styles/transitions';
@@ -340,9 +341,9 @@ SpeedDial.propTypes = {
*/
openIcon: PropTypes.node,
/**
- * Transition component.
+ * The component used for the transition.
*/
- TransitionComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
+ TransitionComponent: componentPropType,
/**
* The duration for the transition, in milliseconds.
* You may specify a single timeout for all transitions, or individually with an object.
diff --git a/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.d.ts b/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.d.ts
index 8ae5484a96f623..5bf7477f54953b 100644
--- a/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.d.ts
+++ b/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.d.ts
@@ -8,6 +8,7 @@ export interface SpeedDialActionProps
ButtonProps?: Partial;
delay?: number;
icon: React.ReactNode;
+ TooltipClasses?: TooltipProps['classes'];
tooltipPlacement?: TooltipProps['placement'];
tooltipTitle?: React.ReactNode;
tooltipOpen?: boolean;
diff --git a/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.js b/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.js
index 933e17e1a91777..a0d5917dd1e75b 100644
--- a/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.js
+++ b/packages/material-ui-lab/src/SpeedDialAction/SpeedDialAction.js
@@ -1,3 +1,5 @@
+// @inheritedComponent Tooltip
+
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
@@ -7,8 +9,6 @@ import Fab from '@material-ui/core/Fab';
import Tooltip from '@material-ui/core/Tooltip';
export const styles = theme => ({
- /* Styles applied to the root element. */
- root: {},
/* Styles applied to the `Button` component. */
button: {
margin: 8,
@@ -75,6 +75,7 @@ class SpeedDialAction extends React.Component {
onKeyDown,
open,
tooltipTitle,
+ TooltipClasses,
tooltipPlacement,
tooltipOpen,
...other
@@ -103,8 +104,8 @@ class SpeedDialAction extends React.Component {
placement={tooltipPlacement}
onClose={this.handleTooltipClose}
onOpen={this.handleTooltipOpen}
- className={classes.root}
open={open && this.state.tooltipOpen}
+ classes={TooltipClasses}
{...other}
>
', () => {
assert.strictEqual(wrapper.type(), Tooltip);
});
+ it('should be able to change the Tooltip classes', () => {
+ const wrapper = shallow( );
+ wrapper.setProps({ TooltipClasses: { root: 'bar' } });
+ assert.include(wrapper.props().classes.root, 'bar');
+ });
+
it('should render a Button', () => {
const wrapper = shallow( );
const buttonWrapper = wrapper.childAt(0);
diff --git a/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.js b/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.js
index 7fd0a0f69aca67..199ee50e920653 100644
--- a/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.js
+++ b/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.js
@@ -34,7 +34,6 @@ class ToggleButtonGroup extends React.Component {
if (value && index >= 0) {
newValue = [...value];
newValue.splice(index, 1);
- if (newValue.length === 0) newValue = null;
} else {
newValue = value ? [...value, buttonValue] : [buttonValue];
}
@@ -120,7 +119,7 @@ ToggleButtonGroup.propTypes = {
* @param {object} event The event source of the callback
* @param {object} value of the selected buttons. When `exclusive` is true
* this is a single value; when false an array of selected values. If no value
- * is selected the value is null.
+ * is selected and `exclusive` is true the value is null; when false an empty array.
*/
onChange: PropTypes.func,
/**
@@ -138,7 +137,6 @@ ToggleButtonGroup.propTypes = {
ToggleButtonGroup.defaultProps = {
exclusive: false,
selected: 'auto',
- value: null,
};
export default withStyles(styles, { name: 'MuiToggleButtonGroup' })(ToggleButtonGroup);
diff --git a/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.test.js b/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.test.js
index 06a15467a1e82d..fe7b86229d04ed 100644
--- a/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.test.js
+++ b/packages/material-ui-lab/src/ToggleButtonGroup/ToggleButtonGroup.test.js
@@ -61,7 +61,7 @@ describe(' ', () => {
it('should not render a selected div when selected is "auto" and a value is missing', () => {
const wrapper = shallow(
-
+
,
);
@@ -178,7 +178,7 @@ describe(' ', () => {
});
describe('non exclusive', () => {
- it('should be null when current value is toggled off', () => {
+ it('should be an empty array when current value is toggled off', () => {
const handleChange = spy();
const wrapper = mount(
@@ -193,7 +193,8 @@ describe(' ', () => {
.simulate('click');
assert.strictEqual(handleChange.callCount, 1);
- assert.strictEqual(handleChange.args[0][1], null);
+ assert.ok(Array.isArray(handleChange.args[0][1]));
+ assert.strictEqual(handleChange.args[0][1].length, 0);
});
it('should be an array with a single value when value is toggled on', () => {
diff --git a/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.js b/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.js
index 68504ba0e561c8..3f858f78d41ef3 100644
--- a/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.js
+++ b/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.js
@@ -4,5 +4,5 @@ export default function hasValue(value) {
return value.length > 0;
}
- return !!value;
+ return value != null;
}
diff --git a/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.test.js b/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.test.js
index 2ab9b6f35f51cb..2585219d0f338c 100644
--- a/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.test.js
+++ b/packages/material-ui-lab/src/ToggleButtonGroup/hasValue.test.js
@@ -21,4 +21,8 @@ describe(' hasValue', () => {
it('should be false for null', () => {
assert.strictEqual(hasValue(null), false);
});
+
+ it('should be true for empty string', () => {
+ assert.strictEqual(hasValue(''), true);
+ });
});
diff --git a/packages/material-ui-styles/package.json b/packages/material-ui-styles/package.json
index 930a5b67a6d9a2..bbc3362c43b2bb 100644
--- a/packages/material-ui-styles/package.json
+++ b/packages/material-ui-styles/package.json
@@ -2,7 +2,7 @@
"name": "@material-ui/styles",
"private": false,
"author": "Material-UI Team",
- "version": "3.0.0-alpha.0",
+ "version": "3.0.0-alpha.3",
"description": "Material-UI Styles - The styling solution of Material-UI.",
"main": "./src/index.js",
"keywords": [
@@ -39,8 +39,7 @@
"@babel/runtime": "7.1.2",
"@material-ui/utils": "^3.0.0-alpha.0",
"classnames": "^2.2.5",
- "deepmerge": "^2.0.1",
- "hoist-non-react-statics": "^3.0.0",
+ "deepmerge": "^3.0.0",
"jss": "^9.3.3",
"jss-camel-case": "^6.0.0",
"jss-default-unit": "^8.0.2",
diff --git a/packages/material-ui-styles/src/StylesProvider.js b/packages/material-ui-styles/src/StylesProvider.js
index b32cf451d08889..16b08d57cc925b 100644
--- a/packages/material-ui-styles/src/StylesProvider.js
+++ b/packages/material-ui-styles/src/StylesProvider.js
@@ -88,7 +88,9 @@ StylesProvider.propTypes = {
sheetsRegistry: PropTypes.object,
};
-StylesProvider.propTypes = exactProp(StylesProvider.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ StylesProvider.propTypes = exactProp(StylesProvider.propTypes);
+}
StylesProvider.defaultProps = {
disableGeneration: false,
diff --git a/packages/material-ui-styles/src/ThemeProvider.js b/packages/material-ui-styles/src/ThemeProvider.js
index 5c569ccd00113a..c85c6aa249b4d2 100644
--- a/packages/material-ui-styles/src/ThemeProvider.js
+++ b/packages/material-ui-styles/src/ThemeProvider.js
@@ -1,16 +1,9 @@
-/* eslint-disable no-underscore-dangle */
-
import React from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
-import { exactProp, ponyfillGlobal } from '@material-ui/utils';
-
-// In order to have self-supporting components, we rely on default theme when not provided.
-ponyfillGlobal.__MUI_DEFAULT_THEME__ = ponyfillGlobal.__MUI_DEFAULT_THEME__ || {};
+import { exactProp } from '@material-ui/utils';
-const defaultTheme = ponyfillGlobal.__MUI_DEFAULT_THEME__;
-
-export const ThemeContext = React.createContext(defaultTheme);
+export const ThemeContext = React.createContext(null);
// To support composition of theme.
function mergeOuterLocalTheme(outerTheme, localTheme) {
@@ -52,7 +45,7 @@ function ThemeProvider(props) {
{outerTheme => {
const theme =
- outerTheme === defaultTheme ? localTheme : mergeOuterLocalTheme(outerTheme, localTheme);
+ outerTheme === null ? localTheme : mergeOuterLocalTheme(outerTheme, localTheme);
return {children} ;
}}
@@ -71,6 +64,8 @@ ThemeProvider.propTypes = {
theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
};
-ThemeProvider.propTypes = exactProp(ThemeProvider.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ ThemeProvider.propTypes = exactProp(ThemeProvider.propTypes);
+}
export default ThemeProvider;
diff --git a/packages/material-ui-styles/src/createGenerateClassName.js b/packages/material-ui-styles/src/createGenerateClassName.js
index 44a01e78d6442b..56432917e42102 100644
--- a/packages/material-ui-styles/src/createGenerateClassName.js
+++ b/packages/material-ui-styles/src/createGenerateClassName.js
@@ -1,5 +1,3 @@
-/* eslint-disable no-underscore-dangle */
-
import warning from 'warning';
const escapeRegex = /([[\].#*$><+~=|^:(),"'`\s])/g;
diff --git a/packages/material-ui-styles/src/hoistInternalStatics.js b/packages/material-ui-styles/src/hoistInternalStatics.js
new file mode 100644
index 00000000000000..9015336b5e8057
--- /dev/null
+++ b/packages/material-ui-styles/src/hoistInternalStatics.js
@@ -0,0 +1,18 @@
+/**
+ * Copies internal immediate statics from material-ui from source to target
+ */
+export default function hoistStatics(target, source) {
+ const internals = ['muiName'];
+
+ for (let i = 0; i < internals.length; i += 1) {
+ const key = internals[i];
+ const descriptor = Object.getOwnPropertyDescriptor(source, key);
+ try {
+ Object.defineProperty(target, key, descriptor);
+ } catch (e) {
+ // Avoid failures from read-only properties and undefined descriptors
+ }
+ }
+
+ return target;
+}
diff --git a/packages/material-ui-styles/src/index.d.ts b/packages/material-ui-styles/src/index.d.ts
index e93282484b9a86..4e4a2b9f09a574 100644
--- a/packages/material-ui-styles/src/index.d.ts
+++ b/packages/material-ui-styles/src/index.d.ts
@@ -108,15 +108,31 @@ declare module '@material-ui/styles/makeStyles' {
ClassNameMap,
PropsOfStyles,
Styles,
+ WithStylesOptions,
} from '@material-ui/styles/withStyles';
+ // https://stackoverflow.com/a/49928360/3406963 without generic branch types
+ type IsAny = 0 extends (1 & T) ? true : false;
+
+ /**
+ * @internal
+ *
+ * `Props` are `any` either by explicit annotation or if there are no callbacks
+ * from which the typechecker could infer a type so it falls back to `any`.
+ * See the test cases for examples and implications of explicit `any` annotation
+ */
+ export type StylesHook> = IsAny> extends true
+ ? (props?: any) => ClassNameMap>
+ : (props: PropsOfStyles) => ClassNameMap>;
+
export default function makeStyles>(
styles: S,
- ): (props: PropsOfStyles) => ClassNameMap>;
+ options?: WithStylesOptions>,
+ ): StylesHook;
}
declare module '@material-ui/styles/styled' {
- import { ConsistentWith, Omit, PropsOf } from '@material-ui/core';
+ import { Omit, PropsOf } from '@material-ui/core';
import {
CSSProperties,
StyledComponentProps,
@@ -139,9 +155,7 @@ declare module '@material-ui/styles/styled' {
className: string;
}
- export default function styled<
- C extends React.ReactType, { className: string }>>
- >(Component: C): ComponentCreator;
+ export default function styled(Component: C): ComponentCreator;
}
declare module '@material-ui/styles/StylesProvider' {
@@ -200,9 +214,10 @@ declare module '@material-ui/styles/withStyles' {
* @internal
* This is basically the API of JSS. It defines a Map,
* where
- *
* - the `keys` are the class (names) that will be created
* - the `values` are objects that represent CSS rules (`React.CSSProperties`).
+ *
+ * if only `CSSProperties` are matched `Props` are inferred to `any`
*/
export type StyleRules = Record<
ClassKey,
diff --git a/packages/material-ui-styles/src/index.spec.tsx b/packages/material-ui-styles/src/index.spec.tsx
index 9ec7277b7c332c..2aa297c2eae652 100644
--- a/packages/material-ui-styles/src/index.spec.tsx
+++ b/packages/material-ui-styles/src/index.spec.tsx
@@ -47,7 +47,7 @@ function testGetThemeProps(theme: Theme, props: AppBarProps): void {
return Greeted?: {defaulted.startsWith('Hello')}
;
}
}
- const StyledMyComponent = styled(MyComponent)((theme: MyTheme) => ({
+ const StyledMyComponent = styled(MyComponent)((theme: MyTheme) => ({
fontFamily: theme.fontFamily,
}));
const renderedMyComponent = (
@@ -56,4 +56,10 @@ function testGetThemeProps(theme: Theme, props: AppBarProps): void {
>
);
+
+ // will not catch type mismatch
+ interface ClassNumberProps {
+ className: number;
+ }
+ styled(({ className }: ClassNumberProps) => {className.toFixed(2)}
)({});
}
diff --git a/packages/material-ui-styles/src/withStyles.js b/packages/material-ui-styles/src/withStyles.js
index 7820849a4d9e28..cff81d1d9be324 100644
--- a/packages/material-ui-styles/src/withStyles.js
+++ b/packages/material-ui-styles/src/withStyles.js
@@ -1,16 +1,14 @@
-/* eslint-disable no-underscore-dangle */
-
import React from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
import getDynamicStyles from 'jss/lib/utils/getDynamicStyles';
import { getDisplayName } from '@material-ui/utils';
-import hoistNonReactStatics from 'hoist-non-react-statics';
import { increment } from './indexCounter';
import mergeClasses from './mergeClasses';
import multiKeyStore from './multiKeyStore';
import getStylesCreator from './getStylesCreator';
import getThemeProps from './getThemeProps';
+import hoistStatics from './hoistInternalStatics';
import { StylesContext } from './StylesProvider';
import { ThemeContext } from './ThemeProvider';
@@ -175,7 +173,7 @@ export function detach({ state, theme, stylesOptions, stylesCreator }) {
// It does not modify the component passed to it;
// instead, it returns a new component, with a `classes` property.
const withStyles = (stylesOrCreator, options = {}) => Component => {
- const { withTheme = false, name, ...stylesOptions2 } = options;
+ const { withTheme = false, name, defaultTheme, ...stylesOptions2 } = options;
const stylesCreator = getStylesCreator(stylesOrCreator);
const listenToTheme = stylesCreator.themingEnabled || typeof name === 'string' || withTheme;
@@ -300,11 +298,21 @@ const withStyles = (stylesOrCreator, options = {}) => Component => {
return listenToTheme ? (
{theme => (
-
+
)}
) : (
-
+
);
}}
@@ -325,7 +333,7 @@ const withStyles = (stylesOrCreator, options = {}) => Component => {
WithStyles.displayName = `WithStyles(${getDisplayName(Component)})`;
}
- hoistNonReactStatics(WithStyles, Component);
+ hoistStatics(WithStyles, Component);
if (process.env.NODE_ENV !== 'production') {
// Exposed for test purposes.
diff --git a/packages/material-ui-styles/src/withStyles.test.js b/packages/material-ui-styles/src/withStyles.test.js
new file mode 100644
index 00000000000000..73d4e7e1302672
--- /dev/null
+++ b/packages/material-ui-styles/src/withStyles.test.js
@@ -0,0 +1,23 @@
+import { assert } from 'chai';
+import React from 'react';
+import { Input } from '@material-ui/core';
+import { isMuiElement } from '@material-ui/core/utils/reactHelpers';
+import withStyles from './withStyles';
+
+describe('withStyles', () => {
+ it('does not hoist statics', () => {
+ const Test = () => null;
+ Test.someStatic = 'will not get hoisted';
+ const TestWithStyles = withStyles({})(Test);
+ assert.strictEqual(TestWithStyles.someStatic, undefined);
+ });
+
+ it('hoists mui internals', () => {
+ assert.strictEqual(isMuiElement( , ['Input']), true);
+
+ // the imported Input is decorated with @material-ui/core/styles
+ const StyledInput = withStyles({})(Input);
+
+ assert.strictEqual(isMuiElement( , ['Input']), true);
+ });
+});
diff --git a/packages/material-ui-styles/src/withTheme.js b/packages/material-ui-styles/src/withTheme.js
index da2fc0695006ac..820630f3e38113 100644
--- a/packages/material-ui-styles/src/withTheme.js
+++ b/packages/material-ui-styles/src/withTheme.js
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import hoistNonReactStatics from 'hoist-non-react-statics';
import { getDisplayName } from '@material-ui/utils';
+import hoistStatics from './hoistInternalStatics';
import { ThemeContext } from './ThemeProvider';
// Provide the theme object as a property to the input component.
@@ -26,7 +26,7 @@ const withTheme = () => Component => {
WithTheme.displayName = `WithTheme(${getDisplayName(Component)})`;
}
- hoistNonReactStatics(WithTheme, Component);
+ hoistStatics(WithTheme, Component);
return WithTheme;
};
diff --git a/packages/material-ui-styles/src/withTheme.test.js b/packages/material-ui-styles/src/withTheme.test.js
index 4eaf0f45366fe9..5da506e72829c9 100644
--- a/packages/material-ui-styles/src/withTheme.test.js
+++ b/packages/material-ui-styles/src/withTheme.test.js
@@ -1,6 +1,8 @@
import React from 'react';
import { assert } from 'chai';
import { createMount } from '@material-ui/core/test-utils';
+import { Input } from '@material-ui/core';
+import { isMuiElement } from '@material-ui/core/utils/reactHelpers';
import PropTypes from 'prop-types';
import withTheme from './withTheme';
import ThemeProvider from './ThemeProvider';
@@ -34,4 +36,19 @@ describe('withTheme', () => {
);
assert.strictEqual(wrapper.text(), 'foo');
});
+
+ it('does not hoist statics', () => {
+ const Test = () => null;
+ Test.someStatic = 'will not get hoisted';
+ const TestWithTheme = withTheme()(Test);
+ assert.strictEqual(TestWithTheme.someStatic, undefined);
+ });
+
+ it('hoists mui internals', () => {
+ assert.strictEqual(isMuiElement( , ['Input']), true);
+
+ const ThemedInput = withTheme()(Input);
+
+ assert.strictEqual(isMuiElement( , ['Input']), true);
+ });
});
diff --git a/packages/material-ui-styles/test/index.spec.tsx b/packages/material-ui-styles/test/index.spec.tsx
index 5c6b47ad21d1e8..e10ad30ade66b2 100644
--- a/packages/material-ui-styles/test/index.spec.tsx
+++ b/packages/material-ui-styles/test/index.spec.tsx
@@ -83,6 +83,8 @@ function testGetThemeProps(theme: Theme, props: AppBarProps): void {
const MyComponent = (props: MyComponentProps) => {
const { color, message } = props;
+ // Expected 1 argument, but got 0
+ const emptyClasses = useMyStyles(); // $ExpectError
const classes = useMyStyles(props);
// $ExpectError
const invalidClasses = useMyStyles({ colourTypo: 'red' });
@@ -95,6 +97,46 @@ function testGetThemeProps(theme: Theme, props: AppBarProps): void {
);
};
+
+ // testing options
+ makeStyles(styles, {
+ flip: true,
+ name: 'some-sheet',
+ generateClassName: (_, sheet) => (sheet ? sheet.classes.root : 'no-sheet'),
+ });
+ makeStyles(styles, {
+ // Property 'toot' does not exist on type 'Record<"root", string>'
+ generateClassName: (_, sheet) => (sheet ? sheet.classes.toot : 'no-sheet'), // $ExpectError
+ });
+
+ // optional props
+ const useWithoutProps = makeStyles((theme: Theme) =>
+ createStyles({
+ root: {
+ background: 'none',
+ },
+ }),
+ );
+ const NoPropsComponent = () => {
+ const classes = useWithoutProps();
+ const alsoClasses = useWithoutProps(5);
+ };
+
+ // unsafe any props make the param optional
+ const useUnsafeProps = makeStyles(
+ createStyles({
+ root: (props: any) => ({
+ backgroundColor: props.deep.color,
+ }),
+ }),
+ );
+
+ const UnsafeProps = (props: StyleProps) => {
+ // would be nice to have at least a compile time error because we forgot the argument
+ const classes = useUnsafeProps(); // runtime: Can't read property color of undefined
+ // but this would pass anyway
+ const alsoClasses = useUnsafeProps(undefined); // runtime: Can't read property color of undefined
+ };
}
// styled
diff --git a/packages/material-ui-utils/package.json b/packages/material-ui-utils/package.json
index 11ddb958a82e6a..9b54b85d01476b 100644
--- a/packages/material-ui-utils/package.json
+++ b/packages/material-ui-utils/package.json
@@ -36,7 +36,8 @@
"react-dom": "^16.3.0"
},
"dependencies": {
- "@babel/runtime": "7.1.2"
+ "@babel/runtime": "7.1.2",
+ "react-is": "^16.6.3"
},
"devDependencies": {},
"sideEffects": false,
diff --git a/packages/material-ui-utils/src/componentPropType.js b/packages/material-ui-utils/src/componentPropType.js
new file mode 100644
index 00000000000000..843b22be22d0a6
--- /dev/null
+++ b/packages/material-ui-utils/src/componentPropType.js
@@ -0,0 +1,48 @@
+const ReactIs = require('react-is');
+
+/**
+ * A factory that returns a propTypes validator that only accepts values that
+ * are also accepted by React.createElement
+ * e.g. "div", functional, class components, forwardRef etc.
+ *
+ * @param {boolean} isRequired If `true` returns a validator
+ * that will throw if nullish values are passed
+ */
+function createComponentProp(isRequired) {
+ /* istanbul ignore if */
+ if (process.env.NODE_ENV === 'production') {
+ return () => null;
+ }
+
+ return function componentPropType(props, key, componentName, location, propFullName) {
+ const prop = props[key];
+ const propName = propFullName || key;
+ let message;
+
+ if (prop == null) {
+ if (isRequired) {
+ message =
+ `The ${location} \`${propName}\` is marked as required in \`${componentName}\`, ` +
+ `but its value is \`${typeof prop}\`.`;
+ }
+ } else if (!ReactIs.isValidElementType(prop)) {
+ const preciseType = typeof prop;
+ message =
+ `Invalid ${location} \`${propName}\` of type \`${preciseType}\` ` +
+ `supplied to \`${componentName}\`, expected a component.`;
+ }
+
+ if (message != null) {
+ // change error message slightly on every check to prevent caching when testing
+ // which would not trigger console errors on subsequent fails
+ return new Error(`${message}${process.env.NODE_ENV === 'test' ? Date.now() : ''}`);
+ }
+
+ return null;
+ };
+}
+
+const componentPropType = createComponentProp(false);
+componentPropType.isRequired = createComponentProp(true);
+
+export default componentPropType;
diff --git a/packages/material-ui-utils/src/componentPropType.test.js b/packages/material-ui-utils/src/componentPropType.test.js
new file mode 100644
index 00000000000000..07eb63cdbed750
--- /dev/null
+++ b/packages/material-ui-utils/src/componentPropType.test.js
@@ -0,0 +1,92 @@
+import { assert } from 'chai';
+import PropTypes from 'prop-types';
+import React from 'react';
+import consoleErrorMock from 'test/utils/consoleErrorMock';
+import componentPropType from './componentPropType';
+
+describe('componentPropType', () => {
+ function testPropType(value, validator, expectedError) {
+ const propName = 'children';
+ const componentName = 'ComponentName';
+ const location = 'prop';
+
+ PropTypes.checkPropTypes(
+ {
+ [propName]: validator,
+ },
+ {
+ [propName]: value,
+ },
+ location,
+ componentName,
+ );
+
+ if (expectedError != null) {
+ assert.strictEqual(consoleErrorMock.callCount(), 1);
+ assert.include(consoleErrorMock.args()[0][0], expectedError);
+ } else {
+ assert.strictEqual(consoleErrorMock.callCount(), 0);
+ }
+ }
+
+ beforeEach(() => {
+ consoleErrorMock.spy();
+ });
+
+ afterEach(() => {
+ consoleErrorMock.reset();
+ });
+
+ it('describe .isRequired', () => {
+ it('rejectes null', () => {
+ testPropType(
+ undefined,
+ componentPropType.isRequired,
+ 'The prop `children` is marked as required in `ComponentName`, ' +
+ 'but its value is `undefined`.',
+ );
+ });
+
+ it('rejects undefined', () => {
+ testPropType(
+ null,
+ componentPropType.isRequired,
+ 'The prop `children` is marked as required in `ComponentName`, but its value is `object`.',
+ );
+ });
+ });
+
+ it('supports optional props', () => {
+ testPropType(undefined, componentPropType, null);
+ testPropType(null, componentPropType, null);
+ });
+
+ it('accepts strings, class and functional components', () => {
+ // eslint-disable-next-line react/prefer-stateless-function
+ class ClassComponent extends React.Component {
+ render() {
+ return null;
+ }
+ }
+
+ testPropType(ClassComponent, componentPropType, null);
+ testPropType(() => null, componentPropType, null);
+ testPropType('will accept any string though', componentPropType, null);
+ });
+
+ it('rejects other types with their type hint', () => {
+ testPropType(
+ 1,
+ componentPropType,
+ 'Invalid prop `children` of type `number` supplied to `ComponentName`, expected a component',
+ );
+ });
+
+ it('rejects objects', () => {
+ testPropType(
+ {},
+ componentPropType,
+ 'Invalid prop `children` of type `object` supplied to `ComponentName`, expected a component',
+ );
+ });
+});
diff --git a/packages/material-ui-utils/src/exactProp.js b/packages/material-ui-utils/src/exactProp.js
index 126042a753a7a3..151de036215cb7 100644
--- a/packages/material-ui-utils/src/exactProp.js
+++ b/packages/material-ui-utils/src/exactProp.js
@@ -13,7 +13,6 @@ function exactProp(propTypes) {
return {
...propTypes,
- // eslint-disable-next-line prefer-arrow-callback
[specialProperty]: props => {
const unsupportedProps = Object.keys(props).filter(prop => !propTypes.hasOwnProperty(prop));
if (unsupportedProps.length > 0) {
diff --git a/packages/material-ui-utils/src/index.js b/packages/material-ui-utils/src/index.js
index 7bcd93f8359f96..4160c12336e574 100644
--- a/packages/material-ui-utils/src/index.js
+++ b/packages/material-ui-utils/src/index.js
@@ -1,3 +1,4 @@
+export { default as componentPropType } from './componentPropType';
export { default as exactProp } from './exactProp';
export { default as getDisplayName } from './getDisplayName';
export { default as ponyfillGlobal } from './ponyfillGlobal';
diff --git a/packages/material-ui/.size-snapshot.json b/packages/material-ui/.size-snapshot.json
index a45af2518f9f69..80f1f6d1b84307 100644
--- a/packages/material-ui/.size-snapshot.json
+++ b/packages/material-ui/.size-snapshot.json
@@ -1,7 +1,7 @@
{
"build/umd/material-ui.production.min.js": {
- "bundled": 859176,
- "minified": 319543,
- "gzipped": 85080
+ "bundled": 869787,
+ "minified": 323723,
+ "gzipped": 85914
}
}
diff --git a/packages/material-ui/package.json b/packages/material-ui/package.json
index 30836d1829608a..3b5ef10f9f8411 100644
--- a/packages/material-ui/package.json
+++ b/packages/material-ui/package.json
@@ -2,7 +2,7 @@
"name": "@material-ui/core",
"private": false,
"author": "Material-UI Team",
- "version": "3.5.1",
+ "version": "3.6.2",
"description": "React components that implement Google's Material Design.",
"keywords": [
"react",
@@ -43,9 +43,9 @@
"classnames": "^2.2.5",
"csstype": "^2.5.2",
"debounce": "^1.1.0",
- "deepmerge": "^2.0.1",
+ "deepmerge": "^3.0.0",
"dom-helpers": "^3.2.1",
- "hoist-non-react-statics": "^3.0.0",
+ "hoist-non-react-statics": "^3.2.1",
"is-plain-object": "^2.0.4",
"jss": "^9.3.3",
"jss-camel-case": "^6.0.0",
diff --git a/packages/material-ui/src/Avatar/Avatar.js b/packages/material-ui/src/Avatar/Avatar.js
index 8ef2121684a2ad..c74d1bf2d277d5 100644
--- a/packages/material-ui/src/Avatar/Avatar.js
+++ b/packages/material-ui/src/Avatar/Avatar.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
export const styles = theme => ({
@@ -20,7 +21,6 @@ export const styles = theme => ({
userSelect: 'none',
},
/* Styles applied to the root element if there are children and not `src` or `srcSet` */
- /* Styles applied to the root element if `color="default"`. */
colorDefault: {
color: theme.palette.background.default,
backgroundColor:
@@ -51,16 +51,10 @@ function Avatar(props) {
...other
} = props;
- const className = classNames(
- classes.root,
- {
- [classes.colorDefault]: childrenProp && !src && !srcSet,
- },
- classNameProp,
- );
let children = null;
+ const img = src || srcSet;
- if (src || srcSet) {
+ if (img) {
children = (
);
} else if (childrenClassNameProp && React.isValidElement(childrenProp)) {
- const childrenClassName = classNames(childrenClassNameProp, childrenProp.props.className);
- children = React.cloneElement(childrenProp, { className: childrenClassName });
+ children = React.cloneElement(childrenProp, {
+ className: classNames(childrenClassNameProp, childrenProp.props.className),
+ });
} else {
children = childrenProp;
}
return (
-
+
{children}
);
@@ -118,7 +122,7 @@ Avatar.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Attributes applied to the `img` element if the component
* is used to display an image.
diff --git a/packages/material-ui/src/Avatar/Avatar.test.js b/packages/material-ui/src/Avatar/Avatar.test.js
index 9e679056f49cea..e5437c5bb2e42d 100644
--- a/packages/material-ui/src/Avatar/Avatar.test.js
+++ b/packages/material-ui/src/Avatar/Avatar.test.js
@@ -134,4 +134,31 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.colorDefault), true);
});
});
+
+ describe('falsey avatar', () => {
+ let wrapper;
+
+ before(() => {
+ wrapper = shallow(
+
+ {0}
+ ,
+ );
+ });
+
+ it('should render with defaultColor class when supplied with a child with falsey value', () => {
+ assert.strictEqual(wrapper.name(), 'div');
+ assert.strictEqual(wrapper.text(), '0');
+ });
+
+ it('should merge user classes & spread custom props to the root node', () => {
+ assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass('my-avatar'), true);
+ assert.strictEqual(wrapper.props()['data-my-prop'], 'woofAvatar');
+ });
+
+ it('should apply the colorDefault class', () => {
+ assert.strictEqual(wrapper.hasClass(classes.colorDefault), true);
+ });
+ });
});
diff --git a/packages/material-ui/src/Badge/Badge.js b/packages/material-ui/src/Badge/Badge.js
index 38272a74ccb915..a38a85916b39d4 100644
--- a/packages/material-ui/src/Badge/Badge.js
+++ b/packages/material-ui/src/Badge/Badge.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
@@ -116,7 +117,7 @@ Badge.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the badge will be invisible.
*/
diff --git a/packages/material-ui/src/BottomNavigationAction/BottomNavigationAction.js b/packages/material-ui/src/BottomNavigationAction/BottomNavigationAction.js
index f397a2f3f47699..d8c50b8315c1df 100644
--- a/packages/material-ui/src/BottomNavigationAction/BottomNavigationAction.js
+++ b/packages/material-ui/src/BottomNavigationAction/BottomNavigationAction.js
@@ -13,10 +13,7 @@ export const styles = theme => ({
transition: theme.transitions.create(['color', 'padding-top'], {
duration: theme.transitions.duration.short,
}),
- paddingTop: 8,
- paddingBottom: 10,
- paddingLeft: 12,
- paddingRight: 12,
+ padding: '6px 12px 8px',
minWidth: 80,
maxWidth: 168,
color: theme.palette.text.secondary,
diff --git a/packages/material-ui/src/Button/Button.js b/packages/material-ui/src/Button/Button.js
index 6c26c41d9e64ce..3f83f6cf5ed0da 100644
--- a/packages/material-ui/src/Button/Button.js
+++ b/packages/material-ui/src/Button/Button.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { fade } from '../styles/colorManipulator';
import ButtonBase from '../ButtonBase';
@@ -45,7 +46,9 @@ export const styles = theme => ({
justifyContent: 'inherit',
},
/* Styles applied to the root element if `variant="text"`. */
- text: {},
+ text: {
+ padding: theme.spacing.unit,
+ },
/* Styles applied to the root element if `variant="text"` and `color="primary"`. */
textPrimary: {
color: theme.palette.primary.main,
@@ -250,9 +253,9 @@ function Button(props) {
[classes.text]: text,
[classes.textPrimary]: text && color === 'primary',
[classes.textSecondary]: text && color === 'secondary',
- [classes.flat]: variant === 'text' || variant === 'flat',
- [classes.flatPrimary]: (variant === 'text' || variant === 'flat') && color === 'primary',
- [classes.flatSecondary]: (variant === 'text' || variant === 'flat') && color === 'secondary',
+ [classes.flat]: text,
+ [classes.flatPrimary]: text && color === 'primary',
+ [classes.flatSecondary]: text && color === 'secondary',
[classes.contained]: contained || fab,
[classes.containedPrimary]: (contained || fab) && color === 'primary',
[classes.containedSecondary]: (contained || fab) && color === 'secondary',
@@ -305,7 +308,7 @@ Button.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the button will be disabled.
*/
@@ -357,25 +360,25 @@ Button.propTypes = {
props => {
if (props.variant === 'flat') {
return new Error(
- 'The `flat` variant will be removed in the next major release. ' +
+ 'Material-UI: the `flat` variant will be removed in the next major release. ' +
'`text` is equivalent and should be used instead.',
);
}
if (props.variant === 'raised') {
return new Error(
- 'The `raised` variant will be removed in the next major release. ' +
+ 'Material-UI: the `raised` variant will be removed in the next major release. ' +
'`contained` is equivalent and should be used instead.',
);
}
if (props.variant === 'fab') {
return new Error(
- 'The `fab` variant will be removed in the next major release. ' +
+ 'Material-UI: the `fab` variant will be removed in the next major release. ' +
'The `` component is equivalent and should be used instead.',
);
}
if (props.variant === 'extendedFab') {
return new Error(
- 'The `fab` variant will be removed in the next major release. ' +
+ 'Material-UI: the `fab` variant will be removed in the next major release. ' +
'The `` component with `variant="extended"` is equivalent ' +
'and should be used instead.',
);
diff --git a/packages/material-ui/src/Button/Button.test.js b/packages/material-ui/src/Button/Button.test.js
index 220b5163cd25f3..edfd981f6be11a 100644
--- a/packages/material-ui/src/Button/Button.test.js
+++ b/packages/material-ui/src/Button/Button.test.js
@@ -28,6 +28,7 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.root), true);
assert.strictEqual(wrapper.hasClass(classes.flat), true);
assert.strictEqual(wrapper.hasClass(classes.fab), false);
+ assert.strictEqual(wrapper.hasClass(classes.text), true);
assert.strictEqual(wrapper.hasClass(classes.textPrimary), false);
assert.strictEqual(wrapper.hasClass(classes.textSecondary), false);
assert.strictEqual(wrapper.hasClass(classes.flatPrimary), false);
@@ -75,6 +76,7 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.root), true);
assert.strictEqual(wrapper.hasClass(classes.contained), true);
assert.strictEqual(wrapper.hasClass(classes.fab), false);
+ assert.strictEqual(wrapper.hasClass(classes.text), false);
assert.strictEqual(wrapper.hasClass(classes.textPrimary), false);
assert.strictEqual(wrapper.hasClass(classes.textSecondary), false);
});
@@ -88,6 +90,7 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.root), true);
assert.strictEqual(wrapper.hasClass(classes.contained), true);
assert.strictEqual(wrapper.hasClass(classes.fab), false);
+ assert.strictEqual(wrapper.hasClass(classes.text), false);
assert.strictEqual(wrapper.hasClass(classes.containedPrimary), true);
assert.strictEqual(wrapper.hasClass(classes.containedSecondary), false);
});
@@ -101,6 +104,7 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.root), true);
assert.strictEqual(wrapper.hasClass(classes.contained), true);
assert.strictEqual(wrapper.hasClass(classes.fab), false);
+ assert.strictEqual(wrapper.hasClass(classes.text), false);
assert.strictEqual(wrapper.hasClass(classes.containedPrimary), false);
assert.strictEqual(wrapper.hasClass(classes.containedSecondary), true);
});
@@ -108,6 +112,7 @@ describe(' ', () => {
it('should render a small button', () => {
const wrapper = shallow(Hello World );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.text), true);
assert.strictEqual(wrapper.hasClass(classes.sizeSmall), true);
assert.strictEqual(wrapper.hasClass(classes.sizeLarge), false);
});
@@ -115,6 +120,7 @@ describe(' ', () => {
it('should render a large button', () => {
const wrapper = shallow(Hello World );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.text), true);
assert.strictEqual(wrapper.hasClass(classes.sizeSmall), false);
assert.strictEqual(wrapper.hasClass(classes.sizeLarge), true);
});
@@ -134,6 +140,7 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.contained), true);
assert.strictEqual(wrapper.hasClass(classes.raised), true);
assert.strictEqual(wrapper.hasClass(classes.fab), false);
+ assert.strictEqual(wrapper.hasClass(classes.text), false);
assert.strictEqual(wrapper.hasClass(classes.containedPrimary), false);
assert.strictEqual(wrapper.hasClass(classes.raisedPrimary), false);
assert.strictEqual(wrapper.hasClass(classes.containedSecondary), false);
@@ -150,6 +157,7 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.contained), true);
assert.strictEqual(wrapper.hasClass(classes.raised), true);
assert.strictEqual(wrapper.hasClass(classes.fab), false);
+ assert.strictEqual(wrapper.hasClass(classes.text), false);
assert.strictEqual(wrapper.hasClass(classes.containedPrimary), true);
assert.strictEqual(wrapper.hasClass(classes.raisedPrimary), true);
assert.strictEqual(wrapper.hasClass(classes.containedSecondary), false);
diff --git a/packages/material-ui/src/ButtonBase/ButtonBase.js b/packages/material-ui/src/ButtonBase/ButtonBase.js
index 8f78a98f44c594..9a4cdfa53fc91c 100644
--- a/packages/material-ui/src/ButtonBase/ButtonBase.js
+++ b/packages/material-ui/src/ButtonBase/ButtonBase.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import ReactDOM from 'react-dom';
import classNames from 'classnames';
import keycode from 'keycode';
+import { componentPropType } from '@material-ui/utils';
import ownerWindow from '../utils/ownerWindow';
import withStyles from '../styles/withStyles';
import NoSsr from '../NoSsr';
@@ -88,6 +89,8 @@ class ButtonBase extends React.Component {
handleTouchMove = createRippleHandler(this, 'TouchMove', 'stop');
+ handleContextMenu = createRippleHandler(this, 'ContextMenu', 'stop');
+
handleBlur = createRippleHandler(this, 'Blur', 'stop', () => {
clearTimeout(this.focusVisibleTimeout);
if (this.state.focusVisible) {
@@ -297,6 +300,7 @@ class ButtonBase extends React.Component {
onTouchEnd={this.handleTouchEnd}
onTouchMove={this.handleTouchMove}
onTouchStart={this.handleTouchStart}
+ onContextMenu={this.handleContextMenu}
ref={buttonRef}
tabIndex={disabled ? '-1' : tabIndex}
{...buttonProps}
@@ -350,7 +354,7 @@ ButtonBase.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the base button will be disabled.
*/
diff --git a/packages/material-ui/src/CardContent/CardContent.js b/packages/material-ui/src/CardContent/CardContent.js
index 1786a5e4ce761f..c795f32bd9430c 100644
--- a/packages/material-ui/src/CardContent/CardContent.js
+++ b/packages/material-ui/src/CardContent/CardContent.js
@@ -1,18 +1,18 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
-export const styles = theme => ({
+export const styles = {
/* Styles applied to the root element. */
- root: theme.mixins.gutters({
- paddingTop: 16,
- paddingBottom: 16,
+ root: {
+ padding: 16,
'&:last-child': {
paddingBottom: 24,
},
- }),
-});
+ },
+};
function CardContent(props) {
const { classes, className, component: Component, ...other } = props;
@@ -34,7 +34,7 @@ CardContent.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
};
CardContent.defaultProps = {
diff --git a/packages/material-ui/src/CardHeader/CardHeader.js b/packages/material-ui/src/CardHeader/CardHeader.js
index b1fe8c8740af5a..a54baaf755c584 100644
--- a/packages/material-ui/src/CardHeader/CardHeader.js
+++ b/packages/material-ui/src/CardHeader/CardHeader.js
@@ -1,17 +1,17 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import Typography from '../Typography';
export const styles = theme => ({
/* Styles applied to the root element. */
- root: theme.mixins.gutters({
+ root: {
display: 'flex',
alignItems: 'center',
- paddingTop: 16,
- paddingBottom: 16,
- }),
+ padding: 16,
+ },
/* Styles applied to the avatar element. */
avatar: {
flex: '0 0 auto',
@@ -116,7 +116,7 @@ CardHeader.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the children won't be wrapped by a Typography component.
* This can be useful to render an alternative Typography variant by wrapping
diff --git a/packages/material-ui/src/CardMedia/CardMedia.js b/packages/material-ui/src/CardMedia/CardMedia.js
index 4159f1832d544c..0bb015ab6f8dd4 100644
--- a/packages/material-ui/src/CardMedia/CardMedia.js
+++ b/packages/material-ui/src/CardMedia/CardMedia.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import warning from 'warning';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
export const styles = {
@@ -62,7 +63,7 @@ CardMedia.propTypes = {
* Component for rendering image.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Image to be displayed as a background image.
* Either `image` or `src` prop must be specified.
diff --git a/packages/material-ui/src/Chip/Chip.js b/packages/material-ui/src/Chip/Chip.js
index a38057ec9195be..72496491afec7d 100644
--- a/packages/material-ui/src/Chip/Chip.js
+++ b/packages/material-ui/src/Chip/Chip.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import keycode from 'keycode';
import warning from 'warning';
+import { componentPropType } from '@material-ui/utils';
import CancelIcon from '../internal/svg-icons/Cancel';
import withStyles from '../styles/withStyles';
import { emphasize, fade } from '../styles/colorManipulator';
@@ -426,7 +427,7 @@ Chip.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Override the default delete icon element. Shown only if `onDelete` is set.
*/
diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
index 89e786432c0570..942c6719101f1d 100644
--- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
+++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.js
@@ -13,6 +13,8 @@ import ownerDocument from '../utils/ownerDocument';
class ClickAwayListener extends React.Component {
mounted = false;
+ moved = false;
+
componentDidMount() {
// Finds the first child when a component returns a fragment.
// https://github.com/facebook/react/blob/036ae3c6e2f056adffc31dfb78d1b6f0c63272f0/packages/react-dom/src/__tests__/ReactDOMFiber-test.js#L105
@@ -35,6 +37,12 @@ class ClickAwayListener extends React.Component {
return;
}
+ // Do not act if user performed touchmove
+ if (this.moved) {
+ this.moved = false;
+ return;
+ }
+
// The child might render null.
if (!this.node) {
return;
@@ -51,6 +59,10 @@ class ClickAwayListener extends React.Component {
}
};
+ handleTouchMove = () => {
+ this.moved = true;
+ };
+
render() {
const { children, mouseEvent, touchEvent, onClickAway, ...other } = this.props;
const listenerProps = {};
@@ -59,6 +71,7 @@ class ClickAwayListener extends React.Component {
}
if (touchEvent !== false) {
listenerProps[touchEvent] = this.handleClickAway;
+ listenerProps.onTouchMove = this.handleTouchMove;
}
return (
diff --git a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.test.js b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.test.js
index d889ac20f13f8f..39113c1146d8ad 100644
--- a/packages/material-ui/src/ClickAwayListener/ClickAwayListener.test.js
+++ b/packages/material-ui/src/ClickAwayListener/ClickAwayListener.test.js
@@ -5,6 +5,16 @@ import { spy } from 'sinon';
import { createMount } from '@material-ui/core/test-utils';
import ClickAwayListener from './ClickAwayListener';
+function fireBodyMouseEvent(name, properties = {}) {
+ const event = document.createEvent('MouseEvents');
+ event.initEvent(name, true, true);
+ Object.keys(properties).forEach(key => {
+ event[key] = properties[key];
+ });
+ document.body.dispatchEvent(event);
+ return event;
+}
+
describe(' ', () => {
let mount;
let wrapper;
@@ -36,9 +46,7 @@ describe(' ', () => {
,
);
- const event = document.createEvent('MouseEvents');
- event.initEvent('mouseup', true, true);
- window.document.body.dispatchEvent(event);
+ const event = fireBodyMouseEvent('mouseup');
assert.strictEqual(handleClickAway.callCount, 1);
assert.deepEqual(handleClickAway.args[0], [event]);
@@ -85,11 +93,7 @@ describe(' ', () => {
Hello
,
);
-
- const event = document.createEvent('MouseEvents');
- event.initEvent('mouseup', true, true);
- window.document.body.dispatchEvent(event);
-
+ fireBodyMouseEvent('mouseup');
assert.strictEqual(handleClickAway.callCount, 0);
});
@@ -100,17 +104,9 @@ describe(' ', () => {
Hello
,
);
-
- const mouseUpEvent = document.createEvent('MouseEvents');
- mouseUpEvent.initEvent('mouseup', true, true);
- window.document.body.dispatchEvent(mouseUpEvent);
-
+ fireBodyMouseEvent('mouseup');
assert.strictEqual(handleClickAway.callCount, 0);
-
- const mouseDownEvent = document.createEvent('MouseEvents');
- mouseDownEvent.initEvent('mousedown', true, true);
- window.document.body.dispatchEvent(mouseDownEvent);
-
+ const mouseDownEvent = fireBodyMouseEvent('mousedown');
assert.strictEqual(handleClickAway.callCount, 1);
assert.deepEqual(handleClickAway.args[0], [mouseDownEvent]);
});
@@ -124,11 +120,7 @@ describe(' ', () => {
Hello
,
);
-
- const event = document.createEvent('Events');
- event.initEvent('touchend', true, true);
- window.document.body.dispatchEvent(event);
-
+ fireBodyMouseEvent('touchend');
assert.strictEqual(handleClickAway.callCount, 0);
});
@@ -139,19 +131,28 @@ describe(' ', () => {
Hello
,
);
-
- const touchEndEvent = document.createEvent('Events');
- touchEndEvent.initEvent('touchend', true, true);
- window.document.body.dispatchEvent(touchEndEvent);
-
+ fireBodyMouseEvent('touchend');
assert.strictEqual(handleClickAway.callCount, 0);
+ const touchStartEvent = fireBodyMouseEvent('touchstart');
+ assert.strictEqual(handleClickAway.callCount, 1);
+ assert.deepEqual(handleClickAway.args[0], [touchStartEvent]);
+ });
- const touchStartEvent = document.createEvent('Events');
- touchStartEvent.initEvent('touchstart', true, true);
- window.document.body.dispatchEvent(touchStartEvent);
+ it('should ignore `touchend` when preceeded by `touchmove` event', () => {
+ const handleClickAway = spy();
+ wrapper = mount(
+
+ Hello
+ ,
+ );
+ fireBodyMouseEvent('touchstart');
+ fireBodyMouseEvent('touchmove');
+ fireBodyMouseEvent('touchend');
+ assert.strictEqual(handleClickAway.callCount, 0);
+ const touchEndEvent = fireBodyMouseEvent('touchend');
assert.strictEqual(handleClickAway.callCount, 1);
- assert.deepEqual(handleClickAway.args[0], [touchStartEvent]);
+ assert.deepEqual(handleClickAway.args[0], [touchEndEvent]);
});
});
@@ -164,11 +165,7 @@ describe(' ', () => {
,
);
wrapper.instance().mounted = false;
-
- const event = document.createEvent('MouseEvents');
- event.initEvent('mouseup', true, true);
- window.document.body.dispatchEvent(event);
-
+ fireBodyMouseEvent('mouseup');
assert.strictEqual(handleClickAway.callCount, 0);
});
});
@@ -181,11 +178,7 @@ describe(' ', () => {
,
);
-
- const event = document.createEvent('MouseEvents');
- event.initEvent('mouseup', true, true);
- window.document.body.dispatchEvent(event);
-
+ fireBodyMouseEvent('mouseup');
assert.strictEqual(handleClickAway.callCount, 0);
});
});
diff --git a/packages/material-ui/src/Collapse/Collapse.js b/packages/material-ui/src/Collapse/Collapse.js
index 552d116f3023a3..0d58cff5eb8501 100644
--- a/packages/material-ui/src/Collapse/Collapse.js
+++ b/packages/material-ui/src/Collapse/Collapse.js
@@ -4,6 +4,7 @@ import React from 'react';
import classNames from 'classnames';
import PropTypes from 'prop-types';
import Transition from 'react-transition-group/Transition';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import { getTransitionProps } from '../transitions/utils';
@@ -203,7 +204,7 @@ Collapse.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the component will transition in.
*/
diff --git a/packages/material-ui/src/CssBaseline/CssBaseline.js b/packages/material-ui/src/CssBaseline/CssBaseline.js
index b5a683b0c6be6d..2f4b4620f59efc 100644
--- a/packages/material-ui/src/CssBaseline/CssBaseline.js
+++ b/packages/material-ui/src/CssBaseline/CssBaseline.js
@@ -48,7 +48,9 @@ CssBaseline.propTypes = {
classes: PropTypes.object.isRequired,
};
-CssBaseline.propTypes = exactProp(CssBaseline.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ CssBaseline.propTypes = exactProp(CssBaseline.propTypes);
+}
CssBaseline.defaultProps = {
children: null,
diff --git a/packages/material-ui/src/Dialog/Dialog.d.ts b/packages/material-ui/src/Dialog/Dialog.d.ts
index c14dd227b954c9..e4dafe6fa60fa2 100644
--- a/packages/material-ui/src/Dialog/Dialog.d.ts
+++ b/packages/material-ui/src/Dialog/Dialog.d.ts
@@ -9,7 +9,7 @@ export interface DialogProps
children?: React.ReactNode;
fullScreen?: boolean;
fullWidth?: boolean;
- maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | false;
+ maxWidth?: 'xs' | 'sm' | 'md' | 'lg' | 'xl' | false;
PaperProps?: Partial;
scroll?: 'body' | 'paper';
TransitionComponent?: React.ReactType;
@@ -21,12 +21,15 @@ export type DialogClassKey =
| 'root'
| 'scrollPaper'
| 'scrollBody'
+ | 'container'
| 'paper'
| 'paperScrollPaper'
| 'paperScrollBody'
| 'paperWidthXs'
| 'paperWidthSm'
| 'paperWidthMd'
+ | 'paperWidthLg'
+ | 'paperWidthXl'
| 'paperFullWidth'
| 'paperFullScreen';
diff --git a/packages/material-ui/src/Dialog/Dialog.js b/packages/material-ui/src/Dialog/Dialog.js
index a77c13582fbc70..960bf00a1affd4 100644
--- a/packages/material-ui/src/Dialog/Dialog.js
+++ b/packages/material-ui/src/Dialog/Dialog.js
@@ -5,11 +5,13 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
import Modal from '../Modal';
import Fade from '../Fade';
import { duration } from '../styles/transitions';
+import chainPropTypes from '../utils/chainPropTypes';
import Paper from '../Paper';
export const styles = theme => ({
@@ -85,6 +87,15 @@ export const styles = theme => ({
},
},
},
+ /* Styles applied to the `Paper` component if `maxWidth="xl"`. */
+ paperWidthXl: {
+ maxWidth: theme.breakpoints.values.xl,
+ '&$paperScrollBody': {
+ [theme.breakpoints.down(theme.breakpoints.values.xl + 48 * 2)]: {
+ margin: 48,
+ },
+ },
+ },
/* Styles applied to the `Paper` component if `fullWidth={true}`. */
paperFullWidth: {
width: '100%',
@@ -142,6 +153,7 @@ class Dialog extends React.Component {
onExited,
onExiting,
open,
+ PaperComponent,
PaperProps,
scroll,
TransitionComponent,
@@ -183,7 +195,7 @@ class Dialog extends React.Component {
onClick={this.handleBackdropClick}
role="document"
>
-
{children}
-
+
@@ -241,7 +253,7 @@ Dialog.propTypes = {
* on the desktop where you might need some coherent different width size across your
* application. Set to `false` to disable `maxWidth`.
*/
- maxWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', false]),
+ maxWidth: PropTypes.oneOf(['xs', 'sm', 'md', 'lg', 'xl', false]),
/**
* Callback fired when the backdrop is clicked.
*/
@@ -250,6 +262,7 @@ Dialog.propTypes = {
* Callback fired when the component requests to be closed.
*
* @param {object} event The event source of the callback
+ * @param {string} reason Can be:`"escapeKeyDown"`, `"backdropClick"`
*/
onClose: PropTypes.func,
/**
@@ -285,18 +298,36 @@ Dialog.propTypes = {
* If `true`, the Dialog is open.
*/
open: PropTypes.bool.isRequired,
+ /**
+ * The component used to render the body of the dialog.
+ */
+ PaperComponent: componentPropType,
/**
* Properties applied to the [`Paper`](/api/paper/) element.
+ * If you want to add a class to the `Paper` component use
+ * `classes.paper` in the `Dialog` props instead.
*/
- PaperProps: PropTypes.object,
+ PaperProps: chainPropTypes(PropTypes.object, props => {
+ const { PaperProps = {} } = props;
+ if ('className' in PaperProps) {
+ return new Error(
+ 'Material-UI: `className` overrides all `Dialog` specific styles in `Paper`. ' +
+ 'If you wanted to add ' +
+ 'styles to the `Paper` component use `classes.paper` in the `Dialog` props ' +
+ `instead.${process.env.NODE_ENV === 'test' ? Date.now() : ''}`,
+ );
+ }
+
+ return null;
+ }),
/**
* Determine the container for scrolling the dialog.
*/
scroll: PropTypes.oneOf(['body', 'paper']),
/**
- * Transition component.
+ * The component used for the transition.
*/
- TransitionComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ TransitionComponent: componentPropType,
/**
* The duration for the transition, in milliseconds.
* You may specify a single timeout for all transitions, or individually with an object.
@@ -317,6 +348,7 @@ Dialog.defaultProps = {
fullScreen: false,
fullWidth: false,
maxWidth: 'sm',
+ PaperComponent: Paper,
scroll: 'paper',
TransitionComponent: Fade,
transitionDuration: { enter: duration.enteringScreen, exit: duration.leavingScreen },
diff --git a/packages/material-ui/src/Dialog/Dialog.test.js b/packages/material-ui/src/Dialog/Dialog.test.js
index afb192bef9bba7..e0b7c9b7f77e70 100644
--- a/packages/material-ui/src/Dialog/Dialog.test.js
+++ b/packages/material-ui/src/Dialog/Dialog.test.js
@@ -1,13 +1,15 @@
import React from 'react';
import { assert } from 'chai';
import { spy } from 'sinon';
-import { createShallow, getClasses } from '@material-ui/core/test-utils';
+import consoleErrorMock from 'test/utils/consoleErrorMock';
+import { createMount, createShallow, getClasses } from '@material-ui/core/test-utils';
import Paper from '../Paper';
import Fade from '../Fade';
import Modal from '../Modal';
import Dialog from './Dialog';
describe(' ', () => {
+ let mount;
let shallow;
let classes;
const defaultProps = {
@@ -15,10 +17,15 @@ describe(' ', () => {
};
before(() => {
+ mount = createMount();
shallow = createShallow({ dive: true });
classes = getClasses(foo );
});
+ after(() => {
+ mount.cleanUp();
+ });
+
it('should render a Modal', () => {
const wrapper = shallow(foo );
assert.strictEqual(wrapper.type(), Modal);
@@ -234,4 +241,32 @@ describe(' ', () => {
assert.strictEqual(wrapper.find(Paper).hasClass(classes.paperFullScreen), false);
});
});
+
+ describe('prop: PaperProps.className', () => {
+ before(() => {
+ consoleErrorMock.spy();
+ });
+
+ after(() => {
+ consoleErrorMock.reset();
+ });
+
+ it('warns on className usage', () => {
+ const wrapper = mount(
+
+ foo
+ ,
+ );
+ const paperWrapper = wrapper.find('div.custom-paper-class');
+
+ assert.strictEqual(paperWrapper.exists(), true);
+ assert.strictEqual(paperWrapper.hasClass(classes.paper), false);
+ assert.strictEqual(consoleErrorMock.callCount(), 1);
+ assert.include(
+ consoleErrorMock.args()[0][0],
+ '`className` overrides all `Dialog` specific styles in `Paper`. If you wanted to add ' +
+ 'styles to the `Paper` component use `classes.paper` in the `Dialog` props instead.',
+ );
+ });
+ });
});
diff --git a/packages/material-ui/src/Divider/Divider.js b/packages/material-ui/src/Divider/Divider.js
index de2845c98d01d8..6d1e433ef95985 100644
--- a/packages/material-ui/src/Divider/Divider.js
+++ b/packages/material-ui/src/Divider/Divider.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { fade } from '../styles/colorManipulator';
import chainPropTypes from '../utils/chainPropTypes';
@@ -83,7 +84,7 @@ Divider.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the divider will be indented.
* __WARNING__: `inset` is deprecated.
diff --git a/packages/material-ui/src/Fab/Fab.js b/packages/material-ui/src/Fab/Fab.js
index 398c7bc1a247b6..3c7eb42338ef6b 100644
--- a/packages/material-ui/src/Fab/Fab.js
+++ b/packages/material-ui/src/Fab/Fab.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import ButtonBase from '../ButtonBase';
import { capitalize } from '../utils/helpers';
@@ -166,7 +167,7 @@ Fab.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the button will be disabled.
*/
diff --git a/packages/material-ui/src/FilledInput/FilledInput.d.ts b/packages/material-ui/src/FilledInput/FilledInput.d.ts
index fee9f1692f7aa8..cf3a998be9688e 100644
--- a/packages/material-ui/src/FilledInput/FilledInput.d.ts
+++ b/packages/material-ui/src/FilledInput/FilledInput.d.ts
@@ -2,7 +2,9 @@ import * as React from 'react';
import { StandardProps, PropTypes } from '..';
import { InputBaseProps } from '../InputBase';
-export interface FilledInputProps extends StandardProps {}
+export interface FilledInputProps extends StandardProps {
+ disableUnderline?: boolean;
+}
export type FilledInputClassKey =
| 'root'
diff --git a/packages/material-ui/src/FilledInput/FilledInput.js b/packages/material-ui/src/FilledInput/FilledInput.js
index e99639a8363828..c2722a3191d38f 100644
--- a/packages/material-ui/src/FilledInput/FilledInput.js
+++ b/packages/material-ui/src/FilledInput/FilledInput.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import InputBase from '../InputBase';
import withStyles from '../styles/withStyles';
@@ -124,13 +125,15 @@ export const styles = theme => {
};
function FilledInput(props) {
- const { classes, ...other } = props;
+ const { disableUnderline, classes, ...other } = props;
return (
', () => {
+ let classes;
let shallow;
+ let mount;
before(() => {
shallow = createShallow({ untilSelector: 'FilledInput' });
+ mount = createMount();
+ classes = getClasses( );
+ });
+
+ after(() => {
+ mount.cleanUp();
});
it('should render a
', () => {
const wrapper = shallow( );
assert.strictEqual(wrapper.type(), InputBase);
+ assert.include(wrapper.props().classes.root, classes.underline);
+ });
+
+ it('should disable the underline', () => {
+ const wrapper = shallow( );
+ assert.notInclude(wrapper.props().classes.root, classes.underline);
});
});
diff --git a/packages/material-ui/src/FormControl/FormControl.js b/packages/material-ui/src/FormControl/FormControl.js
index cccc3db4f2e74d..5199de4c00db3c 100644
--- a/packages/material-ui/src/FormControl/FormControl.js
+++ b/packages/material-ui/src/FormControl/FormControl.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import { isFilled, isAdornedStart } from '../InputBase/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
@@ -173,7 +174,7 @@ FormControl.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the label, input and helper text should be displayed in a disabled state.
*/
diff --git a/packages/material-ui/src/FormControl/FormControl.spec.tsx b/packages/material-ui/src/FormControl/FormControl.spec.tsx
new file mode 100644
index 00000000000000..518f77b9ba3851
--- /dev/null
+++ b/packages/material-ui/src/FormControl/FormControl.spec.tsx
@@ -0,0 +1,12 @@
+import * as React from 'react';
+import FormControl from '@material-ui/core/FormControl';
+
+// custom intrinsic
+// https://github.com/mui-org/material-ui/issues/13744
+{
+ // we only accept DivAttributes since we don't have generic props
+ // however once v4 typings are available this should be accepted #v4-typings
+ ; // $ExpectError
+ // Deafeat device
+ ;
+}
diff --git a/packages/material-ui/src/FormControlLabel/FormControlLabel.js b/packages/material-ui/src/FormControlLabel/FormControlLabel.js
index c762f63a16a8a2..3fe513d3a77653 100644
--- a/packages/material-ui/src/FormControlLabel/FormControlLabel.js
+++ b/packages/material-ui/src/FormControlLabel/FormControlLabel.js
@@ -1,5 +1,3 @@
-/* eslint-disable jsx-a11y/label-has-for */
-
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
diff --git a/packages/material-ui/src/FormControlLabel/FormControlLabel.test.js b/packages/material-ui/src/FormControlLabel/FormControlLabel.test.js
index 8e193cce831809..d2f795240f55fa 100644
--- a/packages/material-ui/src/FormControlLabel/FormControlLabel.test.js
+++ b/packages/material-ui/src/FormControlLabel/FormControlLabel.test.js
@@ -134,7 +134,6 @@ describe(' ', () => {
});
it('should not inject extra properties', () => {
- // eslint-disable-next-line react/prop-types
const Control = ({ inputRef, ...props }) =>
;
const wrapper = mount( } />);
assert.strictEqual(wrapper.find('div').props().name, 'name');
diff --git a/packages/material-ui/src/FormHelperText/FormHelperText.js b/packages/material-ui/src/FormHelperText/FormHelperText.js
index 8d18b43387067b..c43afd52438fc0 100644
--- a/packages/material-ui/src/FormHelperText/FormHelperText.js
+++ b/packages/material-ui/src/FormHelperText/FormHelperText.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import formControlState from '../FormControl/formControlState';
import withFormControlContext from '../FormControl/withFormControlContext';
import withStyles from '../styles/withStyles';
@@ -103,7 +104,7 @@ FormHelperText.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the helper text should be displayed in a disabled state.
*/
diff --git a/packages/material-ui/src/FormLabel/FormLabel.js b/packages/material-ui/src/FormLabel/FormLabel.js
index b5a4312b8d1500..bd8e517cde3450 100644
--- a/packages/material-ui/src/FormLabel/FormLabel.js
+++ b/packages/material-ui/src/FormLabel/FormLabel.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import formControlState from '../FormControl/formControlState';
import withFormControlContext from '../FormControl/withFormControlContext';
import withStyles from '../styles/withStyles';
@@ -109,7 +110,7 @@ FormLabel.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the label should be displayed in a disabled state.
*/
diff --git a/packages/material-ui/src/Grid/Grid.js b/packages/material-ui/src/Grid/Grid.js
index 2915529d2db903..3d08c033d76c54 100644
--- a/packages/material-ui/src/Grid/Grid.js
+++ b/packages/material-ui/src/Grid/Grid.js
@@ -12,6 +12,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { keys as breakpointKeys } from '../styles/createBreakpoints';
import requirePropFactory from '../utils/requirePropFactory';
@@ -274,7 +275,7 @@ Grid.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the component will have the flex *container* behavior.
* You should be wrapping *items* with a *container*.
diff --git a/packages/material-ui/src/GridList/GridList.js b/packages/material-ui/src/GridList/GridList.js
index f00ee08a4d9a70..760ed31d44c952 100644
--- a/packages/material-ui/src/GridList/GridList.js
+++ b/packages/material-ui/src/GridList/GridList.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import warning from 'warning';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
export const styles = {
@@ -93,7 +94,7 @@ GridList.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Number of px for the spacing between tiles.
*/
diff --git a/packages/material-ui/src/GridListTile/GridListTile.js b/packages/material-ui/src/GridListTile/GridListTile.js
index ad9862c8232484..2b52d9ee4200d6 100644
--- a/packages/material-ui/src/GridListTile/GridListTile.js
+++ b/packages/material-ui/src/GridListTile/GridListTile.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import EventListener from 'react-event-listener';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
export const styles = {
@@ -142,7 +143,7 @@ GridListTile.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Height of the tile in number of grid cells.
*/
diff --git a/packages/material-ui/src/Hidden/HiddenJs.js b/packages/material-ui/src/Hidden/HiddenJs.js
index af3e72d3a8a2cc..d00d759215e9a3 100644
--- a/packages/material-ui/src/Hidden/HiddenJs.js
+++ b/packages/material-ui/src/Hidden/HiddenJs.js
@@ -130,6 +130,8 @@ HiddenJs.propTypes = {
xsUp: PropTypes.bool,
};
-HiddenJs.propTypes = exactProp(HiddenJs.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ HiddenJs.propTypes = exactProp(HiddenJs.propTypes);
+}
export default withWidth()(HiddenJs);
diff --git a/packages/material-ui/src/Icon/Icon.js b/packages/material-ui/src/Icon/Icon.js
index c7ba52c9c2609b..f66c643eedff3f 100644
--- a/packages/material-ui/src/Icon/Icon.js
+++ b/packages/material-ui/src/Icon/Icon.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
@@ -93,7 +94,7 @@ Icon.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* The fontSize applied to the icon. Defaults to 24px, but can be configure to inherit font size.
*/
diff --git a/packages/material-ui/src/Input/Input.d.ts b/packages/material-ui/src/Input/Input.d.ts
index a9ec968b64de84..63e998566b6c50 100644
--- a/packages/material-ui/src/Input/Input.d.ts
+++ b/packages/material-ui/src/Input/Input.d.ts
@@ -2,7 +2,9 @@ import * as React from 'react';
import { StandardProps, PropTypes } from '..';
import { InputBaseProps } from '../InputBase';
-export interface InputProps extends StandardProps {}
+export interface InputProps extends StandardProps {
+ disableUnderline?: boolean;
+}
export type InputClassKey =
| 'root'
diff --git a/packages/material-ui/src/Input/Input.js b/packages/material-ui/src/Input/Input.js
index 1df5dc46b4a95e..75d362208cde3c 100644
--- a/packages/material-ui/src/Input/Input.js
+++ b/packages/material-ui/src/Input/Input.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import InputBase from '../InputBase';
import withStyles from '../styles/withStyles';
@@ -70,7 +71,7 @@ export const styles = theme => {
},
},
'&$disabled:before': {
- borderBottom: `1px dotted ${bottomLineColor}`,
+ borderBottomStyle: 'dotted',
},
},
/* Styles applied to the root element if `error={true}`. */
@@ -133,7 +134,15 @@ Input.propTypes = {
/**
* The default input value, useful when not controlling the component.
*/
- defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ defaultValue: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ PropTypes.bool,
+ PropTypes.object,
+ PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool, PropTypes.object]),
+ ),
+ ]),
/**
* If `true`, the input will be disabled.
*/
@@ -163,7 +172,7 @@ Input.propTypes = {
* The component used for the native input.
* Either a string to use a DOM element or a component.
*/
- inputComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ inputComponent: componentPropType,
/**
* Attributes applied to the `input` element.
*/
@@ -228,7 +237,10 @@ Input.propTypes = {
PropTypes.string,
PropTypes.number,
PropTypes.bool,
- PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])),
+ PropTypes.object,
+ PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool, PropTypes.object]),
+ ),
]),
};
diff --git a/packages/material-ui/src/InputAdornment/InputAdornment.js b/packages/material-ui/src/InputAdornment/InputAdornment.js
index 0ca276aecbbe1b..c8f7b2b164aee9 100644
--- a/packages/material-ui/src/InputAdornment/InputAdornment.js
+++ b/packages/material-ui/src/InputAdornment/InputAdornment.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import Typography from '../Typography';
import withStyles from '../styles/withStyles';
@@ -80,7 +81,7 @@ InputAdornment.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If children is a string then disable wrapping in a Typography component.
*/
diff --git a/packages/material-ui/src/InputBase/InputBase.d.ts b/packages/material-ui/src/InputBase/InputBase.d.ts
index 04040ce146f8c1..44c83cce77313a 100644
--- a/packages/material-ui/src/InputBase/InputBase.d.ts
+++ b/packages/material-ui/src/InputBase/InputBase.d.ts
@@ -9,9 +9,8 @@ export interface InputBaseProps
> {
autoComplete?: string;
autoFocus?: boolean;
- defaultValue?: string | number;
+ defaultValue?: Array | string | number | boolean | object;
disabled?: boolean;
- disableUnderline?: boolean;
endAdornment?: React.ReactNode;
error?: boolean;
fullWidth?: boolean;
@@ -40,7 +39,7 @@ export interface InputBaseProps
rowsMax?: string | number;
startAdornment?: React.ReactNode;
type?: string;
- value?: Array | string | number | boolean;
+ value?: Array | string | number | boolean | object;
onFilled?: () => void;
/**
* `onChange`, `onKeyUp` + `onKeyDown` are applied to the inner `InputComponent`,
diff --git a/packages/material-ui/src/InputBase/InputBase.js b/packages/material-ui/src/InputBase/InputBase.js
index 4c8720fe31d3b9..4fb02f6a4c9412 100644
--- a/packages/material-ui/src/InputBase/InputBase.js
+++ b/packages/material-ui/src/InputBase/InputBase.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import formControlState from '../FormControl/formControlState';
import FormControlContext from '../FormControl/FormControlContext';
import withFormControlContext from '../FormControl/withFormControlContext';
@@ -152,8 +153,8 @@ class InputBase extends React.Component {
return null;
}
- constructor(props, context) {
- super(props, context);
+ constructor(props) {
+ super(props);
this.isControlled = props.value != null;
if (this.isControlled) {
this.checkDirty(props);
@@ -278,7 +279,6 @@ class InputBase extends React.Component {
className: classNameProp,
defaultValue,
disabled,
- disableUnderline,
endAdornment,
error,
fullWidth,
@@ -444,7 +444,15 @@ InputBase.propTypes = {
/**
* The default input value, useful when not controlling the component.
*/
- defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ defaultValue: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ PropTypes.bool,
+ PropTypes.object,
+ PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool, PropTypes.object]),
+ ),
+ ]),
/**
* If `true`, the input will be disabled.
*/
@@ -470,7 +478,7 @@ InputBase.propTypes = {
* The component used for the native input.
* Either a string to use a DOM element or a component.
*/
- inputComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ inputComponent: componentPropType,
/**
* Attributes applied to the `input` element.
*/
@@ -567,7 +575,10 @@ InputBase.propTypes = {
PropTypes.string,
PropTypes.number,
PropTypes.bool,
- PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])),
+ PropTypes.object,
+ PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool, PropTypes.object]),
+ ),
]),
};
diff --git a/packages/material-ui/src/InputBase/InputBase.test.js b/packages/material-ui/src/InputBase/InputBase.test.js
index 81a0426d1c8394..a7a973deb5d9ea 100644
--- a/packages/material-ui/src/InputBase/InputBase.test.js
+++ b/packages/material-ui/src/InputBase/InputBase.test.js
@@ -97,15 +97,6 @@ describe(' ', () => {
});
});
- it('should disable the underline', () => {
- const wrapper = mount( );
- const input = wrapper.find('input');
- assert.strictEqual(findOutermostIntrinsic(wrapper).hasClass(classes.inkbar), false);
- assert.strictEqual(input.name(), 'input');
- assert.strictEqual(input.hasClass(classes.input), true);
- assert.strictEqual(input.hasClass(classes.underline), false);
- });
-
it('should fire event callbacks', () => {
const events = ['onChange', 'onFocus', 'onBlur', 'onKeyUp', 'onKeyDown'];
const handlers = events.reduce((result, n) => {
diff --git a/packages/material-ui/src/InputLabel/InputLabel.js b/packages/material-ui/src/InputLabel/InputLabel.js
index 168c345ef0d9be..9c871cd0a8aa81 100644
--- a/packages/material-ui/src/InputLabel/InputLabel.js
+++ b/packages/material-ui/src/InputLabel/InputLabel.js
@@ -54,9 +54,9 @@ export const styles = theme => ({
// zIndex: 1 will raise the label above opaque background-colors of input.
zIndex: 1,
pointerEvents: 'none',
- transform: 'translate(12px, 22px) scale(1)',
+ transform: 'translate(12px, 20px) scale(1)',
'&$marginDense': {
- transform: 'translate(12px, 19px) scale(1)',
+ transform: 'translate(12px, 17px) scale(1)',
},
'&$shrink': {
transform: 'translate(12px, 10px) scale(0.75)',
@@ -70,9 +70,9 @@ export const styles = theme => ({
// see comment above on filled.zIndex
zIndex: 1,
pointerEvents: 'none',
- transform: 'translate(14px, 22px) scale(1)',
+ transform: 'translate(14px, 20px) scale(1)',
'&$marginDense': {
- transform: 'translate(14px, 17.5px) scale(1)',
+ transform: 'translate(14px, 17px) scale(1)',
},
'&$shrink': {
transform: 'translate(14px, -6px) scale(0.75)',
diff --git a/packages/material-ui/src/LinearProgress/LinearProgress.d.ts b/packages/material-ui/src/LinearProgress/LinearProgress.d.ts
index 956ee9f173098a..73a2c3114b7f80 100644
--- a/packages/material-ui/src/LinearProgress/LinearProgress.d.ts
+++ b/packages/material-ui/src/LinearProgress/LinearProgress.d.ts
@@ -13,6 +13,8 @@ export type LinearProgressClassKey =
| 'root'
| 'colorPrimary'
| 'colorSecondary'
+ | 'determinate'
+ | 'indeterminate'
| 'buffer'
| 'query'
| 'dashed'
diff --git a/packages/material-ui/src/LinearProgress/LinearProgress.js b/packages/material-ui/src/LinearProgress/LinearProgress.js
index 44e9e7b9ddc909..4db3707eac35e4 100644
--- a/packages/material-ui/src/LinearProgress/LinearProgress.js
+++ b/packages/material-ui/src/LinearProgress/LinearProgress.js
@@ -23,6 +23,10 @@ export const styles = theme => ({
colorSecondary: {
backgroundColor: lighten(theme.palette.secondary.light, 0.4),
},
+ /* Styles applied to the root element if `variant="determinate"`. */
+ determinate: {},
+ /* Styles applied to the root element if `variant="indeterminate"`. */
+ indeterminate: {},
/* Styles applied to the root element if `variant="buffer"`. */
buffer: {
backgroundColor: 'transparent',
@@ -98,8 +102,6 @@ export const styles = theme => ({
animation: 'mui-indeterminate2 2.1s cubic-bezier(0.165, 0.84, 0.44, 1) infinite',
animationDelay: '1.15s',
},
- /* Styles applied to the bar2 element if `variant="determinate"`. */
- bar2Determinate: {},
/* Styles applied to the bar2 element if `variant="buffer"`. */
bar2Buffer: {
transition: `transform .${TRANSITION_DURATION}s linear`,
@@ -171,6 +173,8 @@ function LinearProgress(props) {
{
[classes.colorPrimary]: color === 'primary',
[classes.colorSecondary]: color === 'secondary',
+ [classes.determinate]: variant === 'determinate',
+ [classes.indeterminate]: variant === 'indeterminate',
[classes.buffer]: variant === 'buffer',
[classes.query]: variant === 'query',
},
@@ -193,7 +197,6 @@ function LinearProgress(props) {
[classes.barColorSecondary]: color === 'secondary' && variant !== 'buffer',
[classes.colorSecondary]: color === 'secondary' && variant === 'buffer',
[classes.bar2Indeterminate]: variant === 'indeterminate' || variant === 'query',
- [classes.bar2Determinate]: variant === 'determinate',
[classes.bar2Buffer]: variant === 'buffer',
});
const rootProps = {};
diff --git a/packages/material-ui/src/LinearProgress/LinearProgress.test.js b/packages/material-ui/src/LinearProgress/LinearProgress.test.js
index 7d13d3203ade6d..67a07ab5711f67 100644
--- a/packages/material-ui/src/LinearProgress/LinearProgress.test.js
+++ b/packages/material-ui/src/LinearProgress/LinearProgress.test.js
@@ -25,9 +25,10 @@ describe(' ', () => {
assert.strictEqual(wrapper.hasClass(classes.root), true);
});
- it('should render intermediate variant by default', () => {
+ it('should render indeterminate variant by default', () => {
const wrapper = shallow( );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.indeterminate), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.barColorPrimary), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.bar1Indeterminate), true);
assert.strictEqual(wrapper.childAt(1).hasClass(classes.barColorPrimary), true);
@@ -51,6 +52,7 @@ describe(' ', () => {
it('should render with determinate classes for the primary color by default', () => {
const wrapper = shallow( );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.determinate), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.barColorPrimary), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.bar1Determinate), true);
});
@@ -58,6 +60,7 @@ describe(' ', () => {
it('should render with determinate classes for the primary color', () => {
const wrapper = shallow( );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.determinate), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.barColorPrimary), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.bar1Determinate), true);
});
@@ -65,6 +68,7 @@ describe(' ', () => {
it('should render with determinate classes for the secondary color', () => {
const wrapper = shallow( );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.determinate), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.barColorSecondary), true);
assert.strictEqual(wrapper.childAt(0).hasClass(classes.bar1Determinate), true);
});
@@ -72,6 +76,7 @@ describe(' ', () => {
it('should set width of bar1 on determinate variant', () => {
const wrapper = shallow( );
assert.strictEqual(wrapper.hasClass(classes.root), true);
+ assert.strictEqual(wrapper.hasClass(classes.determinate), true);
assert.strictEqual(
wrapper.childAt(0).props().style.transform,
'scaleX(0.77)',
diff --git a/packages/material-ui/src/List/List.js b/packages/material-ui/src/List/List.js
index a12395bf22251a..5a0baf8ccc09a9 100644
--- a/packages/material-ui/src/List/List.js
+++ b/packages/material-ui/src/List/List.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import ListContext from './ListContext';
@@ -79,7 +80,7 @@ List.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, compact vertical padding designed for keyboard and mouse input will be used for
* the list and list items. The property is available to descendant components as the
diff --git a/packages/material-ui/src/List/List.spec.tsx b/packages/material-ui/src/List/List.spec.tsx
new file mode 100644
index 00000000000000..d6136cacb4bc74
--- /dev/null
+++ b/packages/material-ui/src/List/List.spec.tsx
@@ -0,0 +1,15 @@
+import * as React from 'react';
+import List from '@material-ui/core/List';
+
+// use other components
+// https://github.com/mui-org/material-ui/issues/13746
+{
+ // HTMLDivElement is not assignable to HTMLUlElement #v4-typings
+
; // $ExpectError
+ {
+ e.currentTarget; // $ExpectType EventTarget & HTMLUListElement
+ }}
+ />;
+}
diff --git a/packages/material-ui/src/ListItem/ListItem.js b/packages/material-ui/src/ListItem/ListItem.js
index f97d0b34e48d70..75bb82a87daf47 100644
--- a/packages/material-ui/src/ListItem/ListItem.js
+++ b/packages/material-ui/src/ListItem/ListItem.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import ButtonBase from '../ButtonBase';
import { isMuiElement } from '../utils/reactHelpers';
@@ -53,7 +54,10 @@ export const styles = theme => ({
backgroundClip: 'padding-box',
},
/* Styles applied to the inner `component` element if `disableGutters={false}`. */
- gutters: theme.mixins.gutters(),
+ gutters: {
+ paddingLeft: 16,
+ paddingRight: 16,
+ },
/* Styles applied to the inner `component` element if `button={true}`. */
button: {
transition: theme.transitions.create('background-color', {
@@ -191,11 +195,11 @@ ListItem.propTypes = {
* Either a string to use a DOM element or a component.
* By default, it's a `li` when `button` is `false` and a `div` when `button` is `true`.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* The container component used when a `ListItemSecondaryAction` is rendered.
*/
- ContainerComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ ContainerComponent: componentPropType,
/**
* Properties applied to the container element when the component
* is used to display a `ListItemSecondaryAction`.
diff --git a/packages/material-ui/src/ListSubheader/ListSubheader.js b/packages/material-ui/src/ListSubheader/ListSubheader.js
index 872796fa191ee0..bea527bb2a0548 100644
--- a/packages/material-ui/src/ListSubheader/ListSubheader.js
+++ b/packages/material-ui/src/ListSubheader/ListSubheader.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
@@ -24,7 +25,10 @@ export const styles = theme => ({
color: 'inherit',
},
/* Styles applied to the inner `component` element if `disableGutters={false}`. */
- gutters: theme.mixins.gutters(),
+ gutters: {
+ paddingLeft: 16,
+ paddingRight: 16,
+ },
/* Styles applied to the root element if `inset={true}`. */
inset: {
paddingLeft: 72,
@@ -89,7 +93,7 @@ ListSubheader.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the List Subheader will not have gutters.
*/
diff --git a/packages/material-ui/src/Menu/Menu.js b/packages/material-ui/src/Menu/Menu.js
index 9c1daddf03a8f6..ba90f8beafc12c 100644
--- a/packages/material-ui/src/Menu/Menu.js
+++ b/packages/material-ui/src/Menu/Menu.js
@@ -38,11 +38,11 @@ class Menu extends React.Component {
}
getContentAnchorEl = () => {
- if (!this.menuListRef || !this.menuListRef.selectedItemRef) {
- return ReactDOM.findDOMNode(this.menuListRef).firstChild;
+ if (this.menuListRef.selectedItemRef) {
+ return ReactDOM.findDOMNode(this.menuListRef.selectedItemRef);
}
- return ReactDOM.findDOMNode(this.menuListRef.selectedItemRef);
+ return ReactDOM.findDOMNode(this.menuListRef).firstChild;
};
focus = () => {
@@ -57,6 +57,10 @@ class Menu extends React.Component {
}
};
+ handleMenuListRef = ref => {
+ this.menuListRef = ref;
+ };
+
handleEntering = element => {
const { disableAutoFocusItem, theme } = this.props;
const menuList = ReactDOM.findDOMNode(this.menuListRef);
@@ -84,7 +88,7 @@ class Menu extends React.Component {
event.preventDefault();
if (this.props.onClose) {
- this.props.onClose(event);
+ this.props.onClose(event, 'tabKeyDown');
}
}
};
@@ -122,9 +126,7 @@ class Menu extends React.Component {
data-mui-test="Menu"
onKeyDown={this.handleListKeyDown}
{...MenuListProps}
- ref={ref => {
- this.menuListRef = ref;
- }}
+ ref={this.handleMenuListRef}
>
{children}
@@ -159,6 +161,7 @@ Menu.propTypes = {
* Callback fired when the component requests to be closed.
*
* @param {object} event The event source of the callback
+ * @param {string} reason Can be:`"escapeKeyDown"`, `"backdropClick"`, `"tabKeyDown"`
*/
onClose: PropTypes.func,
/**
diff --git a/packages/material-ui/src/MenuItem/MenuItem.js b/packages/material-ui/src/MenuItem/MenuItem.js
index 438b5e287cc0b6..b62cad99bf04af 100644
--- a/packages/material-ui/src/MenuItem/MenuItem.js
+++ b/packages/material-ui/src/MenuItem/MenuItem.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import ListItem from '../ListItem';
@@ -68,7 +69,7 @@ MenuItem.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the left and right padding is removed.
*/
diff --git a/packages/material-ui/src/Modal/Modal.js b/packages/material-ui/src/Modal/Modal.js
index 57b3a7556ce275..21ea0e0070ba1a 100644
--- a/packages/material-ui/src/Modal/Modal.js
+++ b/packages/material-ui/src/Modal/Modal.js
@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import warning from 'warning';
import keycode from 'keycode';
+import { componentPropType } from '@material-ui/utils';
import ownerDocument from '../utils/ownerDocument';
import RootRef from '../RootRef';
import Portal from '../Portal';
@@ -124,6 +125,7 @@ class Modal extends React.Component {
handleOpened = () => {
this.autoFocus();
+ this.props.manager.mount(this);
// Fix a bug on Chrome where the scroll isn't initially 0.
this.modalRef.scrollTop = 0;
@@ -298,7 +300,7 @@ class Modal extends React.Component {
', () => {
assert.strictEqual(handleRendered.callCount, 1);
});
});
+
+ describe('two modal at the same time', () => {
+ it('should open and close', () => {
+ const TestCase = props => (
+
+
+ Hello
+
+
+ World
+
+
+ );
+
+ TestCase.propTypes = {
+ open: PropTypes.bool,
+ };
+
+ const wrapper = mount( );
+ assert.strictEqual(document.body.style.overflow, '');
+ wrapper.setProps({ open: true });
+ assert.strictEqual(document.body.style.overflow, 'hidden');
+ wrapper.setProps({ open: false });
+ assert.strictEqual(document.body.style.overflow, '');
+ });
+ });
+
+ it('should support open abort', () => {
+ class TestCase extends React.Component {
+ state = {
+ open: true,
+ };
+
+ componentDidMount() {
+ this.setState({
+ open: false,
+ });
+ }
+
+ render() {
+ return (
+
+ Hello
+
+ );
+ }
+ }
+ mount( );
+ });
});
diff --git a/packages/material-ui/src/Modal/ModalManager.js b/packages/material-ui/src/Modal/ModalManager.js
index f7c4875723e6fc..bd8ead4e0f0ebc 100644
--- a/packages/material-ui/src/Modal/ModalManager.js
+++ b/packages/material-ui/src/Modal/ModalManager.js
@@ -20,23 +20,23 @@ function getPaddingRight(node) {
return parseInt(css(node, 'paddingRight') || 0, 10);
}
-function setContainerStyle(data, container) {
+function setContainerStyle(data) {
const style = { overflow: 'hidden' };
// We are only interested in the actual `style` here because we will override it.
data.style = {
- overflow: container.style.overflow,
- paddingRight: container.style.paddingRight,
+ overflow: data.container.style.overflow,
+ paddingRight: data.container.style.paddingRight,
};
if (data.overflowing) {
const scrollbarSize = getScrollbarSize();
// Use computed style, here to get the real padding to add our scrollbar width.
- style.paddingRight = `${getPaddingRight(container) + scrollbarSize}px`;
+ style.paddingRight = `${getPaddingRight(data.container) + scrollbarSize}px`;
// .mui-fixed is a global helper.
- const fixedNodes = ownerDocument(container).querySelectorAll('.mui-fixed');
+ const fixedNodes = ownerDocument(data.container).querySelectorAll('.mui-fixed');
for (let i = 0; i < fixedNodes.length; i += 1) {
const paddingRight = getPaddingRight(fixedNodes[i]);
data.prevPaddings.push(paddingRight);
@@ -45,14 +45,17 @@ function setContainerStyle(data, container) {
}
Object.keys(style).forEach(key => {
- container.style[key] = style[key];
+ data.container.style[key] = style[key];
});
}
function removeContainerStyle(data) {
- Object.keys(data.style).forEach(key => {
- data.container.style[key] = data.style[key];
- });
+ // The modal might be closed before it had the chance to be mounted in the DOM.
+ if (data.style) {
+ Object.keys(data.style).forEach(key => {
+ data.container.style[key] = data.style[key];
+ });
+ }
const fixedNodes = ownerDocument(data.container).querySelectorAll('.mui-fixed');
for (let i = 0; i < fixedNodes.length; i += 1) {
@@ -115,15 +118,20 @@ class ModalManager {
prevPaddings: [],
};
- if (this.handleContainerOverflow) {
- setContainerStyle(data, container);
- }
-
this.data.push(data);
return modalIdx;
}
+ mount(modal) {
+ const containerIdx = findIndexOf(this.data, item => item.modals.indexOf(modal) !== -1);
+ const data = this.data[containerIdx];
+
+ if (!data.style && this.handleContainerOverflow) {
+ setContainerStyle(data);
+ }
+ }
+
remove(modal) {
const modalIdx = this.modals.indexOf(modal);
diff --git a/packages/material-ui/src/Modal/ModalManager.test.js b/packages/material-ui/src/Modal/ModalManager.test.js
index fbdefc8ff65e9e..e58bedbd7436ae 100644
--- a/packages/material-ui/src/Modal/ModalManager.test.js
+++ b/packages/material-ui/src/Modal/ModalManager.test.js
@@ -20,6 +20,7 @@ describe('ModalManager', () => {
const modal = {};
const modalManager2 = new ModalManager();
const idx = modalManager2.add(modal, container1);
+ modalManager2.mount(modal);
assert.strictEqual(modalManager2.add(modal, container1), idx);
modalManager2.remove(modal);
});
@@ -37,6 +38,7 @@ describe('ModalManager', () => {
it('should add modal1', () => {
const idx = modalManager.add(modal1, container1);
+ modalManager.mount(modal1);
assert.strictEqual(idx, 0, 'should be the first modal');
assert.strictEqual(modalManager.isTopModal(modal1), true);
});
@@ -60,6 +62,7 @@ describe('ModalManager', () => {
it('should add modal2', () => {
const idx = modalManager.add(modal2, container1);
+ modalManager.mount(modal2);
assert.strictEqual(idx, 2, 'should be the "third" modal');
assert.strictEqual(modalManager.isTopModal(modal2), true);
assert.strictEqual(
@@ -111,6 +114,7 @@ describe('ModalManager', () => {
const modal = {};
const paddingRightBefore = container1.style.paddingRight;
modalManager.add(modal, container1);
+ modalManager.mount(modal);
assert.strictEqual(container1.style.overflow, 'hidden');
assert.strictEqual(container1.style.paddingRight, `${getScrollbarSize()}px`);
assert.strictEqual(fixedNode.style.paddingRight, `${14 + getScrollbarSize()}px`);
@@ -140,9 +144,11 @@ describe('ModalManager', () => {
const modal1 = {};
const modal2 = {};
modalManager.add(modal1, container3);
+ modalManager.mount(modal1);
assert.strictEqual(container3.children[0].getAttribute('aria-hidden'), 'true');
modalManager.add(modal2, container4);
+ modalManager.mount(modal2);
assert.strictEqual(container4.children[0].getAttribute('aria-hidden'), 'true');
modalManager.remove(modal2);
@@ -212,6 +218,7 @@ describe('ModalManager', () => {
const modal = { modalRef: container2.children[0] };
modalManager.add(modal, container2);
+ modalManager.mount(modal);
assert.strictEqual(container2.children[0].getAttribute('aria-hidden'), null);
modalManager.remove(modal, container2);
assert.strictEqual(container2.children[0].getAttribute('aria-hidden'), 'true');
diff --git a/packages/material-ui/src/Modal/manageAriaHidden.js b/packages/material-ui/src/Modal/manageAriaHidden.js
index 2ff96a2097c276..04731f6c7bc08e 100644
--- a/packages/material-ui/src/Modal/manageAriaHidden.js
+++ b/packages/material-ui/src/Modal/manageAriaHidden.js
@@ -5,7 +5,7 @@ function isHidable(node) {
}
function siblings(container, mount, currentNode, callback) {
- const blacklist = [mount, currentNode]; // eslint-disable-line no-param-reassign
+ const blacklist = [mount, currentNode];
[].forEach.call(container.children, node => {
if (blacklist.indexOf(node) === -1 && isHidable(node)) {
diff --git a/packages/material-ui/src/NativeSelect/NativeSelect.d.ts b/packages/material-ui/src/NativeSelect/NativeSelect.d.ts
index e46bff7d468de9..6f65e754d06bca 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelect.d.ts
+++ b/packages/material-ui/src/NativeSelect/NativeSelect.d.ts
@@ -9,7 +9,7 @@ export interface NativeSelectProps
Pick {
IconComponent?: React.ReactType;
input?: React.ReactNode;
- value?: string | number | boolean;
+ value?: Array | string | number | boolean;
variant?: 'standard' | 'outlined' | 'filled';
}
diff --git a/packages/material-ui/src/NativeSelect/NativeSelect.js b/packages/material-ui/src/NativeSelect/NativeSelect.js
index 8518a843dbc92f..5333e23f1e9e14 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelect.js
+++ b/packages/material-ui/src/NativeSelect/NativeSelect.js
@@ -2,6 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { componentPropType } from '@material-ui/utils';
import NativeSelectInput from './NativeSelectInput';
import withStyles from '../styles/withStyles';
import formControlState from '../FormControl/formControlState';
@@ -41,6 +42,9 @@ export const styles = theme => ({
'&$disabled': {
cursor: 'default',
},
+ '&[multiple]': {
+ height: 'auto',
+ },
},
/* Styles applied to the `Input` component if `variant="filled"`. */
filled: {
@@ -125,7 +129,7 @@ NativeSelect.propTypes = {
/**
* The icon that displays the arrow.
*/
- IconComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ IconComponent: componentPropType,
/**
* An `Input` element; does not have to be a material-ui specific `Input`.
*/
@@ -148,7 +152,12 @@ NativeSelect.propTypes = {
/**
* The input value.
*/
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
+ value: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ PropTypes.bool,
+ PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])),
+ ]),
/**
* The variant to use.
*/
diff --git a/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts b/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts
index 61a887461d031b..2d8b8534c253e3 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts
+++ b/packages/material-ui/src/NativeSelect/NativeSelectInput.d.ts
@@ -8,7 +8,7 @@ export interface NativeSelectInputProps {
) => void;
name?: string;
onChange?: (event: React.ChangeEvent, child: React.ReactNode) => void;
- value?: string | number | boolean;
+ value?: Array | string | number | boolean;
variant?: 'standard' | 'outlined' | 'filled';
}
diff --git a/packages/material-ui/src/NativeSelect/NativeSelectInput.js b/packages/material-ui/src/NativeSelect/NativeSelectInput.js
index 55b514b8a78532..72420f32f7b64e 100644
--- a/packages/material-ui/src/NativeSelect/NativeSelectInput.js
+++ b/packages/material-ui/src/NativeSelect/NativeSelectInput.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
/**
* @ignore - internal component.
@@ -68,7 +69,7 @@ NativeSelectInput.propTypes = {
/**
* The icon that displays the arrow.
*/
- IconComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ IconComponent: componentPropType,
/**
* Use that property to pass a ref callback to the native select element.
*/
@@ -87,7 +88,12 @@ NativeSelectInput.propTypes = {
/**
* The input value.
*/
- value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool]),
+ value: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ PropTypes.bool,
+ PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])),
+ ]),
/**
* The variant to use.
*/
diff --git a/packages/material-ui/src/NoSsr/NoSsr.js b/packages/material-ui/src/NoSsr/NoSsr.js
index ef7265f9951cee..72d6f35c8a46d8 100644
--- a/packages/material-ui/src/NoSsr/NoSsr.js
+++ b/packages/material-ui/src/NoSsr/NoSsr.js
@@ -34,7 +34,7 @@ class NoSsr extends React.Component {
});
});
} else {
- this.setState({ mounted: true }); // eslint-disable-line react/no-did-mount-set-state
+ this.setState({ mounted: true });
}
}
@@ -62,7 +62,9 @@ NoSsr.propTypes = {
fallback: PropTypes.node,
};
-NoSsr.propTypes = exactProp(NoSsr.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ NoSsr.propTypes = exactProp(NoSsr.propTypes);
+}
NoSsr.defaultProps = {
defer: false,
diff --git a/packages/material-ui/src/OutlinedInput/NotchedOutline.js b/packages/material-ui/src/OutlinedInput/NotchedOutline.js
index 3a44689a46d3cf..1293ad2e7a5f9b 100644
--- a/packages/material-ui/src/OutlinedInput/NotchedOutline.js
+++ b/packages/material-ui/src/OutlinedInput/NotchedOutline.js
@@ -31,7 +31,7 @@ export const styles = theme => {
legend: {
textAlign: 'left',
padding: 0,
- lineHeight: '10px',
+ lineHeight: '11px',
transition: theme.transitions.create('width', {
duration: theme.transitions.duration.shorter,
easing: theme.transitions.easing.easeOut,
diff --git a/packages/material-ui/src/OutlinedInput/OutlinedInput.js b/packages/material-ui/src/OutlinedInput/OutlinedInput.js
index cecff269b7d532..7dc4eb3f4b08c8 100644
--- a/packages/material-ui/src/OutlinedInput/OutlinedInput.js
+++ b/packages/material-ui/src/OutlinedInput/OutlinedInput.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import InputBase from '../InputBase';
import NotchedOutline from './NotchedOutline';
import withStyles from '../styles/withStyles';
@@ -99,7 +100,7 @@ function OutlinedInput(props) {
)}
classes={{
...classes,
- root: classNames(classes.root, classes.underline, {}),
+ root: classNames(classes.root, classes.underline),
notchedOutline: null,
}}
{...other}
@@ -131,7 +132,15 @@ OutlinedInput.propTypes = {
/**
* The default input value, useful when not controlling the component.
*/
- defaultValue: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
+ defaultValue: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.number,
+ PropTypes.bool,
+ PropTypes.object,
+ PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool, PropTypes.object]),
+ ),
+ ]),
/**
* If `true`, the input will be disabled.
*/
@@ -157,7 +166,7 @@ OutlinedInput.propTypes = {
* The component used for the native input.
* Either a string to use a DOM element or a component.
*/
- inputComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ inputComponent: componentPropType,
/**
* Attributes applied to the `input` element.
*/
@@ -230,7 +239,10 @@ OutlinedInput.propTypes = {
PropTypes.string,
PropTypes.number,
PropTypes.bool,
- PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])),
+ PropTypes.object,
+ PropTypes.arrayOf(
+ PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool, PropTypes.object]),
+ ),
]),
};
diff --git a/packages/material-ui/src/Paper/Paper.js b/packages/material-ui/src/Paper/Paper.js
index 78bc030d6f8561..df7c60ad918f9f 100644
--- a/packages/material-ui/src/Paper/Paper.js
+++ b/packages/material-ui/src/Paper/Paper.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import warning from 'warning';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
export const styles = theme => {
@@ -70,7 +71,7 @@ Paper.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Shadow depth, corresponds to `dp` in the spec.
* It's accepting values between 0 and 24 inclusive.
diff --git a/packages/material-ui/src/Popover/Popover.js b/packages/material-ui/src/Popover/Popover.js
index c938584489e30e..3606267e55b365 100644
--- a/packages/material-ui/src/Popover/Popover.js
+++ b/packages/material-ui/src/Popover/Popover.js
@@ -6,8 +6,10 @@ import ReactDOM from 'react-dom';
import warning from 'warning';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
import EventListener from 'react-event-listener';
+import { componentPropType } from '@material-ui/utils';
import ownerDocument from '../utils/ownerDocument';
import ownerWindow from '../utils/ownerWindow';
+import { createChainedFunction } from '../utils/helpers';
import withStyles from '../styles/withStyles';
import Modal from '../Modal';
import Grow from '../Grow';
@@ -92,6 +94,12 @@ class Popover extends React.Component {
if (typeof window !== 'undefined') {
this.handleResize = debounce(() => {
+ // Because we debounce the event, the open property might no longer be true
+ // when the callback resolves.
+ if (!this.props.open) {
+ return;
+ }
+
this.setPositioningStyles(this.paperRef);
}, 166); // Corresponds to 10 frames at 60 Hz.
}
@@ -294,7 +302,7 @@ class Popover extends React.Component {
transformOrigin,
TransitionComponent,
transitionDuration: transitionDurationProp,
- TransitionProps,
+ TransitionProps = {},
...other
} = this.props;
@@ -323,13 +331,13 @@ class Popover extends React.Component {
in={open}
onEnter={onEnter}
onEntered={onEntered}
- onEntering={this.handleEntering}
onExit={onExit}
onExited={onExited}
onExiting={onExiting}
role={role}
timeout={transitionDuration}
{...TransitionProps}
+ onEntering={createChainedFunction(this.handleEntering, TransitionProps.onEntering)}
>
', () => {
const defaultProps = {
open: false,
};
+ const PopoverNaked = unwrap(Popover);
before(() => {
shallow = createShallow({ dive: true });
@@ -602,24 +603,21 @@ describe(' ', () => {
.at(0)
.simulate('resize');
clock.tick(166);
- assert.strictEqual(
- instance.setPositioningStyles.called,
- true,
- 'position styles recalculated',
- );
+ assert.strictEqual(instance.setPositioningStyles.called, true);
});
it('should not recalculate position if the popover is closed', () => {
const wrapper = mount(
-
+
- ,
- );
- assert.strictEqual(
- wrapper.contains('EventListener'),
- false,
- 'no component listening on resize',
+ ,
);
+ const instance = wrapper.instance();
+ assert.strictEqual(wrapper.contains('EventListener'), false);
+ stub(instance, 'setPositioningStyles');
+ wrapper.instance().handleResize();
+ clock.tick(166);
+ assert.strictEqual(instance.setPositioningStyles.called, false);
});
});
@@ -847,4 +845,20 @@ describe(' ', () => {
assert.strictEqual(wrapper.find(TransitionComponent).props().timeout, undefined);
});
});
+
+ describe('prop: TransitionProp', () => {
+ it('should fire Popover transition event callbacks', () => {
+ const handler1 = spy();
+ const handler2 = spy();
+ const wrapper = shallow(
+
+
+ ,
+ );
+
+ wrapper.find(Grow).simulate('entering', { style: {} });
+ assert.strictEqual(handler1.callCount, 1);
+ assert.strictEqual(handler2.callCount, 1);
+ });
+ });
});
diff --git a/packages/material-ui/src/Popper/Popper.js b/packages/material-ui/src/Popper/Popper.js
index c1eea08e7fb242..10c42d03c8218b 100644
--- a/packages/material-ui/src/Popper/Popper.js
+++ b/packages/material-ui/src/Popper/Popper.js
@@ -2,11 +2,12 @@ import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import PopperJS from 'popper.js';
-import withTheme from '../styles/withTheme';
import Portal from '../Portal';
-function flipPlacement(theme, placement) {
- if (theme.direction !== 'rtl') {
+function flipPlacement(placement) {
+ const direction = (typeof window !== 'undefined' && document.body.getAttribute('dir')) || 'ltr';
+
+ if (direction !== 'rtl') {
return placement;
}
@@ -80,15 +81,7 @@ class Popper extends React.Component {
}
handleOpen = () => {
- const {
- anchorEl,
- modifiers,
- open,
- placement,
- popperOptions = {},
- theme,
- disablePortal,
- } = this.props;
+ const { anchorEl, modifiers, open, placement, popperOptions = {}, disablePortal } = this.props;
const popperNode = ReactDOM.findDOMNode(this);
if (!popperNode || !anchorEl || !open) {
@@ -101,7 +94,7 @@ class Popper extends React.Component {
}
this.popper = new PopperJS(getAnchorEl(anchorEl), popperNode, {
- placement: flipPlacement(theme, placement),
+ placement: flipPlacement(placement),
...popperOptions,
modifiers: {
...(disablePortal
@@ -166,7 +159,7 @@ class Popper extends React.Component {
}
const childProps = {
- placement: placement || flipPlacement(theme, placementProps),
+ placement: placement || flipPlacement(placementProps),
};
if (transition) {
@@ -258,10 +251,6 @@ Popper.propTypes = {
* Options provided to the [`popper.js`](https://github.com/FezVrasta/popper.js) instance.
*/
popperOptions: PropTypes.object,
- /**
- * @ignore
- */
- theme: PropTypes.object.isRequired,
/**
* Help supporting a react-transition-group/Transition component.
*/
@@ -274,4 +263,4 @@ Popper.defaultProps = {
transition: false,
};
-export default withTheme()(Popper);
+export default Popper;
diff --git a/packages/material-ui/src/Popper/Popper.test.js b/packages/material-ui/src/Popper/Popper.test.js
index faf512315d72ca..016df77c684660 100644
--- a/packages/material-ui/src/Popper/Popper.test.js
+++ b/packages/material-ui/src/Popper/Popper.test.js
@@ -1,10 +1,7 @@
-/* eslint-disable no-underscore-dangle */
-
import React from 'react';
import { assert } from 'chai';
import { spy } from 'sinon';
import { createShallow, createMount, unwrap } from '@material-ui/core/test-utils';
-import createMuiTheme from '../styles/createMuiTheme';
import Grow from '../Grow';
import Popper from './Popper';
@@ -13,7 +10,6 @@ const PopperNaked = unwrap(Popper);
describe(' ', () => {
const defaultProps = {
open: true,
- theme: createMuiTheme(),
children: Hello World ,
};
let shallow;
@@ -23,7 +19,7 @@ describe(' ', () => {
before(() => {
anchorEl = window.document.createElement('div');
window.document.body.appendChild(anchorEl);
- shallow = createShallow({ dive: true });
+ shallow = createShallow();
mount = createMount();
});
@@ -39,6 +35,14 @@ describe(' ', () => {
});
describe('prop: placement', () => {
+ before(() => {
+ document.body.setAttribute('dir', 'rtl');
+ });
+
+ after(() => {
+ document.body.removeAttribute('dir');
+ });
+
it('should have top placement', () => {
const renderSpy = spy();
shallow(
@@ -53,10 +57,6 @@ describe(' ', () => {
assert.strictEqual(renderSpy.args[0][0], 'top');
});
- const theme = createMuiTheme({
- direction: 'rtl',
- });
-
[
{
in: 'bottom-end',
@@ -82,7 +82,7 @@ describe(' ', () => {
it(`should flip ${test.in} when direction=rtl is used`, () => {
const renderSpy = spy();
shallow(
-
+
{({ placement }) => {
renderSpy(placement);
return null;
diff --git a/packages/material-ui/src/Portal/Portal.js b/packages/material-ui/src/Portal/Portal.js
index c3eca567a68391..fe1bf1e72041f8 100644
--- a/packages/material-ui/src/Portal/Portal.js
+++ b/packages/material-ui/src/Portal/Portal.js
@@ -99,6 +99,8 @@ Portal.defaultProps = {
disablePortal: false,
};
-Portal.propTypes = exactProp(Portal.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ Portal.propTypes = exactProp(Portal.propTypes);
+}
export default Portal;
diff --git a/packages/material-ui/src/RootRef/RootRef.js b/packages/material-ui/src/RootRef/RootRef.js
index 5286c230fa37b7..281b27923a0604 100644
--- a/packages/material-ui/src/RootRef/RootRef.js
+++ b/packages/material-ui/src/RootRef/RootRef.js
@@ -75,6 +75,8 @@ RootRef.propTypes = {
rootRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,
};
-RootRef.propTypes = exactProp(RootRef.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ RootRef.propTypes = exactProp(RootRef.propTypes);
+}
export default RootRef;
diff --git a/packages/material-ui/src/Select/Select.d.ts b/packages/material-ui/src/Select/Select.d.ts
index 217ac5b19c3ebc..1fcd13f3cb3748 100644
--- a/packages/material-ui/src/Select/Select.d.ts
+++ b/packages/material-ui/src/Select/Select.d.ts
@@ -19,7 +19,7 @@ export interface SelectProps
open?: boolean;
renderValue?: (value: SelectProps['value']) => React.ReactNode;
SelectDisplayProps?: React.HTMLAttributes;
- value?: Array | string | number | boolean;
+ value?: Array | string | number | boolean | object;
variant?: 'standard' | 'outlined' | 'filled';
}
diff --git a/packages/material-ui/src/Select/Select.js b/packages/material-ui/src/Select/Select.js
index b5cc2eebc925dd..b6d67d5fb2a67b 100644
--- a/packages/material-ui/src/Select/Select.js
+++ b/packages/material-ui/src/Select/Select.js
@@ -2,6 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { componentPropType } from '@material-ui/utils';
import SelectInput from './SelectInput';
import formControlState from '../FormControl/formControlState';
import withFormControlContext from '../FormControl/withFormControlContext';
@@ -53,13 +54,13 @@ function Select(props) {
IconComponent,
variant: fcs.variant,
type: undefined, // We render a select. We can ignore the type provided by the `Input`.
+ multiple,
...(native
? {}
: {
autoWidth,
displayEmpty,
MenuProps,
- multiple,
onClose,
onOpen,
open,
@@ -104,7 +105,7 @@ Select.propTypes = {
/**
* The icon that displays the arrow.
*/
- IconComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ IconComponent: componentPropType,
/**
* An `Input` element; does not have to be a material-ui specific `Input`.
*/
@@ -196,6 +197,4 @@ Select.defaultProps = {
Select.muiName = 'Select';
-export default withStyles(nativeSelectStyles, { name: 'MuiSelect' })(
- withFormControlContext(Select),
-);
+export default withStyles(styles, { name: 'MuiSelect' })(withFormControlContext(Select));
diff --git a/packages/material-ui/src/Select/Select.test.js b/packages/material-ui/src/Select/Select.test.js
index 35690ca7ebf8f0..dbea6e46cd0584 100644
--- a/packages/material-ui/src/Select/Select.test.js
+++ b/packages/material-ui/src/Select/Select.test.js
@@ -85,4 +85,21 @@ describe(' ', () => {
assert.strictEqual(React.isValidElement(selected), true);
});
});
+
+ describe('prop: value', () => {
+ it('should be able to use an object', () => {
+ const value = {};
+ const wrapper = mount(
+
+
+ None
+
+ Ten
+ Twenty
+ Thirty
+ ,
+ );
+ assert.strictEqual(wrapper.find(`.${classes.select}`).text(), 'Twenty');
+ });
+ });
});
diff --git a/packages/material-ui/src/Select/SelectInput.d.ts b/packages/material-ui/src/Select/SelectInput.d.ts
index 509d24fbde2cf2..a9a01ec79946b5 100644
--- a/packages/material-ui/src/Select/SelectInput.d.ts
+++ b/packages/material-ui/src/Select/SelectInput.d.ts
@@ -23,7 +23,7 @@ export interface SelectInputProps {
renderValue?: (value: SelectInputProps['value']) => React.ReactNode;
SelectDisplayProps?: React.HTMLAttributes;
tabIndex?: number;
- value?: string | number | boolean | Array;
+ value: string | number | boolean | Array;
variant?: 'standard' | 'outlined' | 'filled';
}
diff --git a/packages/material-ui/src/Select/SelectInput.js b/packages/material-ui/src/Select/SelectInput.js
index 61d37cadfa2123..ae99301c011f73 100644
--- a/packages/material-ui/src/Select/SelectInput.js
+++ b/packages/material-ui/src/Select/SelectInput.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import classNames from 'classnames';
import keycode from 'keycode';
import warning from 'warning';
+import { componentPropType } from '@material-ui/utils';
import Menu from '../Menu/Menu';
import { isFilled } from '../InputBase/utils';
import { setRef } from '../utils/reactHelpers';
@@ -369,7 +370,7 @@ SelectInput.propTypes = {
/**
* The icon that displays the arrow.
*/
- IconComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ IconComponent: componentPropType,
/**
* Use that property to pass a ref callback to the native select element.
*/
diff --git a/packages/material-ui/src/Snackbar/Snackbar.js b/packages/material-ui/src/Snackbar/Snackbar.js
index a27fc3131e3529..de0b02c71e6ada 100644
--- a/packages/material-ui/src/Snackbar/Snackbar.js
+++ b/packages/material-ui/src/Snackbar/Snackbar.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import EventListener from 'react-event-listener';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { duration } from '../styles/transitions';
import ClickAwayListener from '../ClickAwayListener';
@@ -382,9 +383,9 @@ Snackbar.propTypes = {
*/
resumeHideDuration: PropTypes.number,
/**
- * Transition component.
+ * The component used for the transition.
*/
- TransitionComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ TransitionComponent: componentPropType,
/**
* The duration for the transition, in milliseconds.
* You may specify a single timeout for all transitions, or individually with an object.
diff --git a/packages/material-ui/src/StepContent/StepContent.js b/packages/material-ui/src/StepContent/StepContent.js
index 5396b0213b105e..d93afac7af7d83 100644
--- a/packages/material-ui/src/StepContent/StepContent.js
+++ b/packages/material-ui/src/StepContent/StepContent.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import Collapse from '../Collapse';
import withStyles from '../styles/withStyles';
@@ -109,9 +110,9 @@ StepContent.propTypes = {
*/
orientation: PropTypes.oneOf(['horizontal', 'vertical']),
/**
- * Collapse component.
+ * The component used for the transition.
*/
- TransitionComponent: PropTypes.func,
+ TransitionComponent: componentPropType,
/**
* Adjust the duration of the content expand transition.
* Passed as a property to the transition component.
diff --git a/packages/material-ui/src/StepLabel/StepLabel.js b/packages/material-ui/src/StepLabel/StepLabel.js
index 1d48296b989214..dad5fa17ad6f80 100644
--- a/packages/material-ui/src/StepLabel/StepLabel.js
+++ b/packages/material-ui/src/StepLabel/StepLabel.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import Typography from '../Typography';
import StepIcon from '../StepIcon';
@@ -194,7 +195,7 @@ StepLabel.propTypes = {
/**
* The component to render in place of the [`StepIcon`](/api/step-icon/).
*/
- StepIconComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ StepIconComponent: componentPropType,
/**
* Properties applied to the [`StepIcon`](/api/step-icon/) element.
*/
diff --git a/packages/material-ui/src/SvgIcon/SvgIcon.js b/packages/material-ui/src/SvgIcon/SvgIcon.js
index 94905515052d30..c8f02932878fb5 100644
--- a/packages/material-ui/src/SvgIcon/SvgIcon.js
+++ b/packages/material-ui/src/SvgIcon/SvgIcon.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
@@ -112,7 +113,7 @@ SvgIcon.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* The fontSize applied to the icon. Defaults to 24px, but can be configure to inherit font size.
*/
diff --git a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js
index f98ade9e9a5b6b..44f9e57b2febbf 100644
--- a/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js
+++ b/packages/material-ui/src/SwipeableDrawer/SwipeableDrawer.test.js
@@ -9,13 +9,14 @@ import SwipeableDrawer, { reset } from './SwipeableDrawer';
import SwipeArea from './SwipeArea';
import createMuiTheme from '../styles/createMuiTheme';
-function fireBodyMouseEvent(name, properties) {
+function fireBodyMouseEvent(name, properties = {}) {
const event = document.createEvent('MouseEvents');
event.initEvent(name, true, true);
Object.keys(properties).forEach(key => {
event[key] = properties[key];
});
document.body.dispatchEvent(event);
+ return event;
}
describe(' ', () => {
diff --git a/packages/material-ui/src/Table/Table.js b/packages/material-ui/src/Table/Table.js
index 8b6aee3204160c..1a331df58580da 100644
--- a/packages/material-ui/src/Table/Table.js
+++ b/packages/material-ui/src/Table/Table.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import TableContext from './TableContext';
@@ -15,14 +16,34 @@ export const styles = theme => ({
},
});
-function Table(props) {
- const { classes, className, component: Component, padding, ...other } = props;
+class Table extends React.Component {
+ memoizedContextValue = {};
- return (
-
-
-
- );
+ // To replace with the corresponding Hook once Material-UI v4.0.0 is out:
+ // https://reactjs.org/docs/hooks-reference.html#usememo
+ useMemo(contextValue) {
+ const objectKeys = Object.keys(contextValue);
+
+ for (let i = 0; i < objectKeys.length; i += 1) {
+ const objectKey = objectKeys[i];
+
+ if (contextValue[objectKey] !== this.memoizedContextValue[objectKey]) {
+ this.memoizedContextValue = contextValue;
+ break;
+ }
+ }
+ return this.memoizedContextValue;
+ }
+
+ render() {
+ const { classes, className, component: Component, padding, ...other } = this.props;
+
+ return (
+
+
+
+ );
+ }
}
Table.propTypes = {
@@ -43,7 +64,7 @@ Table.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* Allows TableCells to inherit padding of the Table.
*/
diff --git a/packages/material-ui/src/TableBody/TableBody.js b/packages/material-ui/src/TableBody/TableBody.js
index a3d5e3bad1763c..95b733cd303c67 100644
--- a/packages/material-ui/src/TableBody/TableBody.js
+++ b/packages/material-ui/src/TableBody/TableBody.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import Tablelvl2Context from '../Table/Tablelvl2Context';
@@ -11,11 +12,13 @@ export const styles = {
},
};
+const contextValue = { variant: 'body' };
+
function TableBody(props) {
const { classes, className, component: Component, ...other } = props;
return (
-
+
);
@@ -39,7 +42,7 @@ TableBody.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
};
TableBody.defaultProps = {
diff --git a/packages/material-ui/src/TableCell/TableCell.js b/packages/material-ui/src/TableCell/TableCell.js
index b34c201ccda35f..bca1c96c5bf4b5 100644
--- a/packages/material-ui/src/TableCell/TableCell.js
+++ b/packages/material-ui/src/TableCell/TableCell.js
@@ -1,8 +1,10 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
+import deprecatedPropType from '../utils/deprecatedPropType';
import { darken, fade, lighten } from '../styles/colorManipulator';
import TableContext from '../Table/TableContext';
import Tablelvl2Context from '../Table/Tablelvl2Context';
@@ -67,16 +69,34 @@ export const styles = theme => ({
padding: 0,
},
},
+ /* Styles applied to the root element if `align="left"`. */
+ alignLeft: {
+ textAlign: 'left',
+ },
+ /* Styles applied to the root element if `align="center"`. */
+ alignCenter: {
+ textAlign: 'center',
+ },
+ /* Styles applied to the root element if `align="right"`. */
+ alignRight: {
+ textAlign: 'right',
+ flexDirection: 'row-reverse',
+ },
+ /* Styles applied to the root element if `align="justify"`. */
+ alignJustify: {
+ textAlign: 'justify',
+ },
});
function TableCell(props) {
const {
+ align,
children,
classes,
className: classNameProp,
component,
sortDirection,
- numeric,
+ numeric = false,
padding: paddingProp,
scope: scopeProp,
variant,
@@ -113,6 +133,7 @@ function TableCell(props) {
[classes.footer]: variant
? variant === 'footer'
: tablelvl2 && tablelvl2.variant === 'footer',
+ [classes[`align${capitalize(align)}`]]: align !== 'inherit',
[classes.numeric]: numeric,
[classes[`padding${capitalize(padding)}`]]: padding !== 'default',
},
@@ -137,6 +158,13 @@ function TableCell(props) {
}
TableCell.propTypes = {
+ /**
+ * Set the text-align on the table cell content.
+ *
+ * Monetary or generally number fields **should be right aligned** as that allows
+ * you to add them up quickly in your head without having to worry about decimals.
+ */
+ align: PropTypes.oneOf(['inherit', 'left', 'center', 'right', 'justify']),
/**
* The table cell contents.
*/
@@ -154,14 +182,11 @@ TableCell.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, content will align to the right.
- *
- * Monetary or generally number fields should be right aligned as that allows
- * you to add them up quickly in your head without having to worry about decimals.
*/
- numeric: PropTypes.bool,
+ numeric: deprecatedPropType(PropTypes.bool, 'Instead, use the `align` property.'),
/**
* Sets the padding applied to the cell.
* By default, the Table parent component set the value.
@@ -183,7 +208,7 @@ TableCell.propTypes = {
};
TableCell.defaultProps = {
- numeric: false,
+ align: 'inherit',
};
export default withStyles(styles, { name: 'MuiTableCell' })(TableCell);
diff --git a/packages/material-ui/src/TableCell/TableCell.test.js b/packages/material-ui/src/TableCell/TableCell.test.js
index 94d47d62a80416..424d9907897413 100644
--- a/packages/material-ui/src/TableCell/TableCell.test.js
+++ b/packages/material-ui/src/TableCell/TableCell.test.js
@@ -83,12 +83,6 @@ describe(' ', () => {
assert.strictEqual(wrapper.find('div').hasClass(classes.root), true);
});
- it('should render with the numeric class', () => {
- const wrapper = mountInTable( );
- assert.strictEqual(wrapper.find('td').hasClass(classes.root), true);
- assert.strictEqual(wrapper.find('td').hasClass(classes.numeric), true);
- });
-
it('should render aria-sort="ascending" when prop sortDirection="asc" provided', () => {
const wrapper = mountInTable( );
assert.strictEqual(wrapper.find('td').props()['aria-sort'], 'ascending');
@@ -98,4 +92,9 @@ describe(' ', () => {
const wrapper = mountInTable( );
assert.strictEqual(wrapper.find('td').props()['aria-sort'], 'descending');
});
+
+ it('should center content', () => {
+ const wrapper = mountInTable( );
+ assert.strictEqual(wrapper.find('td').hasClass(classes.alignCenter), true);
+ });
});
diff --git a/packages/material-ui/src/TableFooter/TableFooter.js b/packages/material-ui/src/TableFooter/TableFooter.js
index 3774a233e5ec78..ad59493abbc23b 100644
--- a/packages/material-ui/src/TableFooter/TableFooter.js
+++ b/packages/material-ui/src/TableFooter/TableFooter.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import Tablelvl2Context from '../Table/Tablelvl2Context';
@@ -11,11 +12,13 @@ export const styles = {
},
};
+const contextValue = { variant: 'footer' };
+
function TableFooter(props) {
const { classes, className, component: Component, ...other } = props;
return (
-
+
);
@@ -39,7 +42,7 @@ TableFooter.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
};
TableFooter.defaultProps = {
diff --git a/packages/material-ui/src/TableHead/TableHead.js b/packages/material-ui/src/TableHead/TableHead.js
index 16c85c1b051e0e..69ef279503496d 100644
--- a/packages/material-ui/src/TableHead/TableHead.js
+++ b/packages/material-ui/src/TableHead/TableHead.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import Tablelvl2Context from '../Table/Tablelvl2Context';
@@ -11,11 +12,13 @@ export const styles = {
},
};
+const contextValue = { variant: 'head' };
+
function TableHead(props) {
const { classes, className, component: Component, ...other } = props;
return (
-
+
);
@@ -39,7 +42,7 @@ TableHead.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
};
TableHead.defaultProps = {
diff --git a/packages/material-ui/src/TablePagination/TablePagination.js b/packages/material-ui/src/TablePagination/TablePagination.js
index d0f754e4506e34..a1f9def312ba5a 100644
--- a/packages/material-ui/src/TablePagination/TablePagination.js
+++ b/packages/material-ui/src/TablePagination/TablePagination.js
@@ -2,6 +2,7 @@
import React from 'react';
import PropTypes from 'prop-types';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import InputBase from '../InputBase';
import MenuItem from '../MenuItem';
@@ -94,7 +95,7 @@ class TablePagination extends React.Component {
page,
rowsPerPage,
rowsPerPageOptions,
- SelectProps,
+ SelectProps = {},
...other
} = this.props;
@@ -104,6 +105,8 @@ class TablePagination extends React.Component {
colSpan = colSpanProp || 1000; // col-span over everything
}
+ const MenuItemComponent = SelectProps.native ? 'option' : MenuItem;
+
return (
@@ -126,13 +129,13 @@ class TablePagination extends React.Component {
{...SelectProps}
>
{rowsPerPageOptions.map(rowsPerPageOption => (
-
{rowsPerPageOption}
-
+
))}
)}
@@ -164,7 +167,7 @@ TablePagination.propTypes = {
* The component used for displaying the actions.
* Either a string to use a DOM element or a component.
*/
- ActionsComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ ActionsComponent: componentPropType,
/**
* Properties applied to the back arrow [`IconButton`](/api/icon-button/) component.
*/
@@ -182,7 +185,7 @@ TablePagination.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* The total number of rows.
*/
diff --git a/packages/material-ui/src/TableRow/TableRow.js b/packages/material-ui/src/TableRow/TableRow.js
index 6e0c0a9c0d66da..404949601f3866 100644
--- a/packages/material-ui/src/TableRow/TableRow.js
+++ b/packages/material-ui/src/TableRow/TableRow.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import Tablelvl2Context from '../Table/Tablelvl2Context';
@@ -91,7 +92,7 @@ TableRow.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the table row will shade on hover.
*/
diff --git a/packages/material-ui/src/TableSortLabel/TableSortLabel.js b/packages/material-ui/src/TableSortLabel/TableSortLabel.js
index 26176a43edb0a8..52378f34fa0973 100644
--- a/packages/material-ui/src/TableSortLabel/TableSortLabel.js
+++ b/packages/material-ui/src/TableSortLabel/TableSortLabel.js
@@ -3,6 +3,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import ArrowDownwardIcon from '../internal/svg-icons/ArrowDownward';
import withStyles from '../styles/withStyles';
import ButtonBase from '../ButtonBase';
@@ -113,7 +114,7 @@ TableSortLabel.propTypes = {
/**
* Sort icon to use.
*/
- IconComponent: PropTypes.func,
+ IconComponent: componentPropType,
};
TableSortLabel.defaultProps = {
diff --git a/packages/material-ui/src/Tabs/ScrollbarSize.js b/packages/material-ui/src/Tabs/ScrollbarSize.js
index 1d74aba5efef7a..a9c845d6b186e7 100644
--- a/packages/material-ui/src/Tabs/ScrollbarSize.js
+++ b/packages/material-ui/src/Tabs/ScrollbarSize.js
@@ -4,11 +4,12 @@ import EventListener from 'react-event-listener';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
const styles = {
- width: 100,
- height: 100,
+ width: 90,
+ height: 90,
position: 'absolute',
- top: -10000,
+ top: -9000,
overflow: 'scroll',
+ // Support IE 11
msOverflowStyle: 'scrollbar',
};
@@ -23,13 +24,11 @@ class ScrollbarSize extends React.Component {
if (typeof window !== 'undefined') {
this.handleResize = debounce(() => {
- const { onChange } = this.props;
-
const prevHeight = this.scrollbarHeight;
- const prevWidth = this.scrollbarWidth;
this.setMeasurements();
- if (prevHeight !== this.scrollbarHeight || prevWidth !== this.scrollbarWidth) {
- onChange({ scrollbarHeight: this.scrollbarHeight, scrollbarWidth: this.scrollbarWidth });
+
+ if (prevHeight !== this.scrollbarHeight) {
+ this.props.onChange(this.scrollbarHeight);
}
}, 166); // Corresponds to 10 frames at 60 Hz.
}
@@ -37,16 +36,17 @@ class ScrollbarSize extends React.Component {
componentDidMount() {
this.setMeasurements();
- this.props.onLoad({
- scrollbarHeight: this.scrollbarHeight,
- scrollbarWidth: this.scrollbarWidth,
- });
+ this.props.onChange(this.scrollbarHeight);
}
componentWillUnmount() {
this.handleResize.clear();
}
+ handleRef = ref => {
+ this.nodeRef = ref;
+ };
+
setMeasurements = () => {
const nodeRef = this.nodeRef;
@@ -55,29 +55,20 @@ class ScrollbarSize extends React.Component {
}
this.scrollbarHeight = nodeRef.offsetHeight - nodeRef.clientHeight;
- this.scrollbarWidth = nodeRef.offsetWidth - nodeRef.clientWidth;
};
render() {
- const { onChange } = this.props;
-
return (
-
- {onChange ?
: null}
-
{
- this.nodeRef = ref;
- }}
- />
-
+
+
+
+
);
}
}
ScrollbarSize.propTypes = {
onChange: PropTypes.func.isRequired,
- onLoad: PropTypes.func.isRequired,
};
export default ScrollbarSize;
diff --git a/packages/material-ui/src/Tabs/ScrollbarSize.test.js b/packages/material-ui/src/Tabs/ScrollbarSize.test.js
index b7f0df0c35e6eb..b306c43de79dce 100644
--- a/packages/material-ui/src/Tabs/ScrollbarSize.test.js
+++ b/packages/material-ui/src/Tabs/ScrollbarSize.test.js
@@ -7,7 +7,6 @@ import ScrollbarSize from './ScrollbarSize';
describe('
', () => {
const defaultProps = {
- onLoad: () => {},
onChange: () => {},
};
let clock;
@@ -20,7 +19,7 @@ describe('
', () => {
clock.restore();
});
- describe('prop: onLoad', () => {
+ describe('mount', () => {
let wrapper;
afterEach(() => {
@@ -28,20 +27,16 @@ describe('
', () => {
});
it('should not call on initial load', () => {
- const onLoad = spy();
+ const onChange = spy();
wrapper = mount(
);
- assert.strictEqual(onLoad.callCount, 0, 'should not have been called');
+ assert.strictEqual(onChange.callCount, 0);
});
it('should call on initial load', () => {
- const onLoad = spy();
- wrapper = mount(
);
- assert.strictEqual(onLoad.callCount, 1, 'should have been called once');
- assert.strictEqual(
- onLoad.calledWith({ scrollbarHeight: 0, scrollbarWidth: 0 }),
- true,
- 'should have been called with expected sizes',
- );
+ const onChange = spy();
+ wrapper = mount(
);
+ assert.strictEqual(onChange.callCount, 1);
+ assert.strictEqual(onChange.calledWith(0), true);
});
});
@@ -56,26 +51,22 @@ describe('
', () => {
instance.nodeRef = {
offsetHeight: 17,
clientHeight: 0,
- offsetWidth: 17,
- clientWidth: 0,
};
});
it('should call on first resize event', () => {
+ assert.strictEqual(onChange.callCount, 1);
wrapper.find(EventListener).simulate('resize');
clock.tick(166);
- assert.strictEqual(onChange.callCount, 1, 'should have been called once');
- assert.strictEqual(
- onChange.calledWith({ scrollbarHeight: 17, scrollbarWidth: 17 }),
- true,
- 'should have been called with expected sizes',
- );
+ assert.strictEqual(onChange.callCount, 2);
+ assert.strictEqual(onChange.calledWith(17), true);
});
it('should not call on second resize event', () => {
+ assert.strictEqual(onChange.callCount, 1);
wrapper.find(EventListener).simulate('resize');
clock.tick(166);
- assert.strictEqual(onChange.callCount, 1, 'should only have been called once');
+ assert.strictEqual(onChange.callCount, 2);
});
});
});
diff --git a/packages/material-ui/src/Tabs/Tabs.js b/packages/material-ui/src/Tabs/Tabs.js
index 698fad1dbda0e3..72ae7ed8e99bec 100644
--- a/packages/material-ui/src/Tabs/Tabs.js
+++ b/packages/material-ui/src/Tabs/Tabs.js
@@ -7,6 +7,7 @@ import classNames from 'classnames';
import EventListener from 'react-event-listener';
import debounce from 'debounce'; // < 1kb payload overhead when lodash/debounce is > 3kb.
import { getNormalizedScrollLeft, detectScrollType } from 'normalize-scroll-left';
+import { componentPropType } from '@material-ui/utils';
import animate from '../internal/animate';
import ScrollbarSize from './ScrollbarSize';
import withStyles from '../styles/withStyles';
@@ -83,7 +84,6 @@ class Tabs extends React.Component {
};
componentDidMount() {
- // eslint-disable-next-line react/no-did-mount-set-state
this.setState({ mounted: true });
this.updateIndicatorState(this.props);
this.updateScrollButtonState();
@@ -115,10 +115,7 @@ class Tabs extends React.Component {
const { classes, scrollable, ScrollButtonComponent, scrollButtons, theme } = this.props;
const conditionalElements = {};
conditionalElements.scrollbarSizeListener = scrollable ? (
-
+
) : null;
const showScrollButtons = scrollable && (scrollButtons === 'auto' || scrollButtons === 'on');
@@ -173,7 +170,7 @@ class Tabs extends React.Component {
tab,
[
`Material-UI: the value provided \`${value}\` to the Tabs component is invalid.`,
- 'Non of the Tabs children have this value.',
+ 'None of the Tabs children have this value.',
this.valueToIndex.keys
? `You can provide one of the following values: ${Array.from(
this.valueToIndex.keys(),
@@ -195,7 +192,7 @@ class Tabs extends React.Component {
this.moveTabsScroll(this.tabsRef.clientWidth);
};
- handleScrollbarSizeChange = ({ scrollbarHeight }) => {
+ handleScrollbarSizeChange = scrollbarHeight => {
this.setState({
scrollerStyle: {
marginBottom: -scrollbarHeight,
@@ -425,7 +422,7 @@ Tabs.propTypes = {
* The component used for the root node.
* Either a string to use a DOM element or a component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the tabs will grow to use all the available space.
* This property is intended for small views, like on mobile.
@@ -450,7 +447,7 @@ Tabs.propTypes = {
/**
* The component used to render the scroll buttons.
*/
- ScrollButtonComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ ScrollButtonComponent: componentPropType,
/**
* Determine behavior of scroll buttons when tabs are set to scroll
* `auto` will only present them on medium and larger viewports
diff --git a/packages/material-ui/src/Tooltip/Tooltip.js b/packages/material-ui/src/Tooltip/Tooltip.js
index 2ce8109563b992..277e2000918f93 100644
--- a/packages/material-ui/src/Tooltip/Tooltip.js
+++ b/packages/material-ui/src/Tooltip/Tooltip.js
@@ -2,6 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import RootRef from '../RootRef';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
@@ -83,7 +84,9 @@ class Tooltip extends React.Component {
componentDidMount() {
warning(
- !this.childrenRef.disabled || !this.childrenRef.tagName.toLowerCase() === 'button',
+ !this.childrenRef.disabled ||
+ (this.childrenRef.disabled && this.props.title === '') ||
+ this.childrenRef.tagName.toLowerCase() !== 'button',
[
'Material-UI: you are providing a disabled `button` child to the Tooltip component.',
'A disabled element does not fire events.',
@@ -278,10 +281,18 @@ class Tooltip extends React.Component {
open = false;
}
+ // For accessibility and SEO concerns, we render the title to the DOM node when
+ // the tooltip is hidden. However, we have made a tradeoff when
+ // `disableHoverListener` is set. This title logic is disabled.
+ // It's allowing us to keep the implementation size minimal.
+ // We are open to change the tradeoff.
+ const shouldShowNativeTitle = !open && !disableHoverListener;
const childrenProps = {
'aria-describedby': open ? id || this.defaultId : null,
- title: !open && typeof title === 'string' ? title : null,
+ title: shouldShowNativeTitle && typeof title === 'string' ? title : null,
...other,
+ ...children.props,
+ className: classNames(other.className, children.props.className),
};
if (!disableTouchListener) {
@@ -451,9 +462,9 @@ Tooltip.propTypes = {
*/
title: PropTypes.node.isRequired,
/**
- * Transition component.
+ * The component used for the transition.
*/
- TransitionComponent: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ TransitionComponent: componentPropType,
/**
* Properties applied to the `Transition` element.
*/
diff --git a/packages/material-ui/src/Tooltip/Tooltip.test.js b/packages/material-ui/src/Tooltip/Tooltip.test.js
index 32f4bb2dc39a48..5adcf47cf27310 100644
--- a/packages/material-ui/src/Tooltip/Tooltip.test.js
+++ b/packages/material-ui/src/Tooltip/Tooltip.test.js
@@ -1,5 +1,3 @@
-/* eslint-disable no-underscore-dangle */
-
import React from 'react';
import { assert } from 'chai';
import { spy, useFakeTimers } from 'sinon';
@@ -40,12 +38,25 @@ describe('
', () => {
const wrapper = shallow(
);
assert.strictEqual(wrapper.type(), React.Fragment);
assert.strictEqual(wrapper.childAt(0).name(), 'RootRef');
- assert.strictEqual(wrapper.childAt(1).name(), 'WithTheme(Popper)');
+ assert.strictEqual(wrapper.childAt(1).name(), 'Popper');
assert.strictEqual(wrapper.childAt(1).hasClass(classes.popper), true);
});
+ describe('prop: disableHoverListener', () => {
+ it('should hide the native title', () => {
+ const wrapper = shallow(
+
+ Hello World
+ ,
+ );
+
+ const children = wrapper.find('button');
+ assert.strictEqual(children.props().title, null);
+ });
+ });
+
describe('prop: title', () => {
- it('should display if the title is presetn', () => {
+ it('should display if the title is present', () => {
const wrapper = shallow(
);
assert.strictEqual(wrapper.find(Popper).props().open, true);
});
@@ -54,6 +65,17 @@ describe('
', () => {
const wrapper = shallow(
);
assert.strictEqual(wrapper.find(Popper).props().open, false);
});
+
+ it('should be passed down to the child as a native title', () => {
+ const wrapper = shallow(
+
+ Hello World
+ ,
+ );
+
+ const children = wrapper.find('button');
+ assert.strictEqual(children.props().title, 'Hello World');
+ });
});
describe('prop: placement', () => {
@@ -218,6 +240,17 @@ describe('
', () => {
consoleErrorMock.reset();
});
+ it('should not raise a warning if title is empty', () => {
+ mount(
+
+
+ Hello World
+
+ ,
+ );
+ assert.strictEqual(consoleErrorMock.callCount(), 0, 'should not call console.error');
+ });
+
it('should raise a warning when we can listen to events', () => {
mount(
@@ -255,12 +288,23 @@ describe(' ', () => {
});
});
- it('should forward properties to the child element', () => {
- const wrapper = shallow(
-
- H1
- ,
- );
- assert.strictEqual(wrapper.find('h1').props().className, 'foo');
+ describe('forward', () => {
+ it('should forward properties to the child element', () => {
+ const wrapper = shallow(
+
+ H1
+ ,
+ );
+ assert.strictEqual(wrapper.find('h1').props().className, 'foo bar');
+ });
+
+ it('should respect the properties priority', () => {
+ const wrapper = shallow(
+
+ H1
+ ,
+ );
+ assert.strictEqual(wrapper.find('h1').props().hidden, false);
+ });
});
});
diff --git a/packages/material-ui/src/Typography/Typography.js b/packages/material-ui/src/Typography/Typography.js
index 12023f3dd26fd0..eefc25509a42aa 100644
--- a/packages/material-ui/src/Typography/Typography.js
+++ b/packages/material-ui/src/Typography/Typography.js
@@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
+import { componentPropType } from '@material-ui/utils';
import withStyles from '../styles/withStyles';
import { capitalize } from '../utils/helpers';
import chainPropTypes from '../utils/chainPropTypes';
@@ -235,7 +236,7 @@ Typography.propTypes = {
* Either a string to use a DOM element or a component.
* By default, it maps the variant to a good default headline component.
*/
- component: PropTypes.oneOfType([PropTypes.string, PropTypes.func, PropTypes.object]),
+ component: componentPropType,
/**
* If `true`, the text will have a bottom margin.
*/
@@ -310,9 +311,9 @@ Typography.propTypes = {
deprecatedVariants.indexOf(props.variant) !== -1
) {
return new Error(
- 'You are using a deprecated typography variant: ' +
- `\`${props.variant}\` that will be removed in the next major release.` +
- '\nPlease read the migration guide under https://material-ui.com/style/typography#migration-to-typography-v2',
+ 'Material-UI: you are using a deprecated typography variant: ' +
+ `\`${props.variant}\` that will be removed in the next major release.\n` +
+ 'Please read the migration guide under https://material-ui.com/style/typography#migration-to-typography-v2.',
);
}
diff --git a/packages/material-ui/src/index.d.ts b/packages/material-ui/src/index.d.ts
index 88acd2ba91c990..db00b3ae4226bd 100644
--- a/packages/material-ui/src/index.d.ts
+++ b/packages/material-ui/src/index.d.ts
@@ -138,6 +138,7 @@ export { default as ExpansionPanel } from './ExpansionPanel';
export { default as ExpansionPanelActions } from './ExpansionPanelActions';
export { default as ExpansionPanelDetails } from './ExpansionPanelDetails';
export { default as ExpansionPanelSummary } from './ExpansionPanelSummary';
+export { default as Fab } from './Fab';
export { default as Fade } from './Fade';
export { default as FilledInput } from './FilledInput';
export { default as FormControl } from './FormControl';
diff --git a/packages/material-ui/src/internal/SwitchBase.js b/packages/material-ui/src/internal/SwitchBase.js
index 32ebacaea1aa36..4f5b2c8415e124 100644
--- a/packages/material-ui/src/internal/SwitchBase.js
+++ b/packages/material-ui/src/internal/SwitchBase.js
@@ -87,6 +87,7 @@ class SwitchBase extends React.Component {
checkedIcon,
classes,
className: classNameProp,
+ defaultChecked,
disabled: disabledProp,
icon,
id,
@@ -137,7 +138,8 @@ class SwitchBase extends React.Component {
{checked ? checkedIcon : icon}
func => () => {
+ global.successOnce = global.successOnce || {};
+
+ if (!global.successOnce[name]) {
+ global.successOnce[name] = false;
+ }
+
+ try {
+ func();
+ global.successOnce[name] = true;
+ } catch (err) {
+ if (!global.successOnce[name]) {
+ throw err;
+ }
+ }
+};
+
describe(' ', () => {
let mount;
let classes;
@@ -145,7 +163,6 @@ describe(' ', () => {
);
wrapperA.setProps({ disabled: false });
- wrapperA.setProps({ checked: true });
assert.strictEqual(
wrapperA.hasClass(disabledClassName),
@@ -430,4 +447,42 @@ describe(' ', () => {
assert.strictEqual(handleFocusContext.callCount, 1);
});
});
+
+ describe('check transitioning between controlled states throws errors', () => {
+ beforeEach(() => {
+ consoleErrorMock.spy();
+ });
+
+ afterEach(() => {
+ consoleErrorMock.reset();
+ });
+
+ it(
+ 'should error when uncontrolled and changed to controlled',
+ shouldSuccessOnce('didWarnUncontrolledToControlled')(() => {
+ const wrapper = mount( );
+ wrapper.setProps({ checked: true });
+
+ assert.strictEqual(consoleErrorMock.callCount(), 1);
+ assert.include(
+ consoleErrorMock.args()[0][0],
+ 'A component is changing an uncontrolled input of type %s to be controlled.',
+ );
+ }),
+ );
+
+ it(
+ 'should error when controlled and changed to uncontrolled',
+ shouldSuccessOnce('didWarnControlledToUncontrolled')(() => {
+ const wrapper = mount( );
+ wrapper.setProps({ checked: undefined });
+
+ assert.strictEqual(consoleErrorMock.callCount(), 1);
+ assert.include(
+ consoleErrorMock.args()[0][0],
+ 'A component is changing a controlled input of type %s to be uncontrolled.',
+ );
+ }),
+ );
+ });
});
diff --git a/packages/material-ui/src/styles/MuiThemeProvider.js b/packages/material-ui/src/styles/MuiThemeProvider.js
index cfd53aa1ec919d..c71086c89ab622 100644
--- a/packages/material-ui/src/styles/MuiThemeProvider.js
+++ b/packages/material-ui/src/styles/MuiThemeProvider.js
@@ -139,7 +139,9 @@ MuiThemeProviderOld.propTypes = {
theme: PropTypes.oneOfType([PropTypes.object, PropTypes.func]).isRequired,
};
-MuiThemeProviderOld.propTypes = exactProp(MuiThemeProviderOld.propTypes);
+if (process.env.NODE_ENV !== 'production') {
+ MuiThemeProviderOld.propTypes = exactProp(MuiThemeProviderOld.propTypes);
+}
MuiThemeProviderOld.childContextTypes = {
...themeListener.contextTypes,
diff --git a/packages/material-ui/src/styles/createGenerateClassName.js b/packages/material-ui/src/styles/createGenerateClassName.js
index 44a01e78d6442b..56432917e42102 100644
--- a/packages/material-ui/src/styles/createGenerateClassName.js
+++ b/packages/material-ui/src/styles/createGenerateClassName.js
@@ -1,5 +1,3 @@
-/* eslint-disable no-underscore-dangle */
-
import warning from 'warning';
const escapeRegex = /([[\].#*$><+~=|^:(),"'`\s])/g;
diff --git a/packages/material-ui/src/styles/overrides.d.ts b/packages/material-ui/src/styles/overrides.d.ts
index 545448aee057d0..ee4f0b8dab1f1a 100644
--- a/packages/material-ui/src/styles/overrides.d.ts
+++ b/packages/material-ui/src/styles/overrides.d.ts
@@ -31,6 +31,7 @@ import { ExpansionPanelClassKey } from '../ExpansionPanel';
import { ExpansionPanelActionsClassKey } from '../ExpansionPanelActions';
import { ExpansionPanelDetailsClassKey } from '../ExpansionPanelDetails';
import { ExpansionPanelSummaryClassKey } from '../ExpansionPanelSummary';
+import { FabClassKey } from '../Fab';
import { FilledInputClassKey } from '../FilledInput';
import { FormControlClassKey } from '../FormControl';
import { FormControlLabelClassKey } from '../FormControlLabel';
@@ -126,6 +127,7 @@ export interface ComponentNameToClassKey {
MuiExpansionPanelActions: ExpansionPanelActionsClassKey;
MuiExpansionPanelDetails: ExpansionPanelDetailsClassKey;
MuiExpansionPanelSummary: ExpansionPanelSummaryClassKey;
+ MuiFab: FabClassKey;
MuiFilledInput: FilledInputClassKey;
MuiFormControl: FormControlClassKey;
MuiFormControlLabel: FormControlLabelClassKey;
diff --git a/packages/material-ui/src/styles/transitions.js b/packages/material-ui/src/styles/transitions.js
index b470b788a1a8c3..03bbc2917d72dd 100644
--- a/packages/material-ui/src/styles/transitions.js
+++ b/packages/material-ui/src/styles/transitions.js
@@ -1,4 +1,3 @@
-/* eslint-disable no-param-reassign */
/* eslint-disable no-restricted-globals */
import warning from 'warning';
diff --git a/packages/material-ui/src/styles/withStyles.js b/packages/material-ui/src/styles/withStyles.js
index a10dcf20f4e76c..052fb28b8c54d4 100644
--- a/packages/material-ui/src/styles/withStyles.js
+++ b/packages/material-ui/src/styles/withStyles.js
@@ -42,22 +42,11 @@ export const sheetsManager = new Map();
const noopTheme = {};
// In order to have self-supporting components, we rely on default theme when not provided.
-let defaultTheme;
-
-function getDefaultTheme() {
- if (defaultTheme) {
- return defaultTheme;
- }
-
- defaultTheme = createMuiTheme({
- typography: {
- suppressWarning: true,
- },
- });
- return defaultTheme;
-}
-
-ponyfillGlobal.__MUI_DEFAULT_THEME__ = getDefaultTheme();
+const defaultTheme = createMuiTheme({
+ typography: {
+ suppressWarning: true,
+ },
+});
// Link a style sheet with a component.
// It does not modify the component passed to it;
@@ -103,7 +92,7 @@ const withStylesOld = (stylesOrCreator, options = {}) => Component => {
...context[ns.sheetOptions],
};
// We use || as the function call is lazy evaluated.
- this.theme = listenToTheme ? themeListener.initial(context) || getDefaultTheme() : noopTheme;
+ this.theme = listenToTheme ? themeListener.initial(context) || defaultTheme : noopTheme;
this.attach(this.theme);
@@ -339,4 +328,8 @@ if (!ponyfillGlobal.__MUI_STYLES__.withStyles) {
ponyfillGlobal.__MUI_STYLES__.withStyles = withStylesOld;
}
-export default ponyfillGlobal.__MUI_STYLES__.withStyles;
+export default (styles, options) =>
+ ponyfillGlobal.__MUI_STYLES__.withStyles(styles, {
+ defaultTheme,
+ ...options,
+ });
diff --git a/packages/material-ui/src/styles/withTheme.js b/packages/material-ui/src/styles/withTheme.js
index c884bbe8bef16a..27e38a2377c737 100644
--- a/packages/material-ui/src/styles/withTheme.js
+++ b/packages/material-ui/src/styles/withTheme.js
@@ -14,7 +14,11 @@ function getDefaultTheme() {
return defaultTheme;
}
- defaultTheme = createMuiTheme();
+ defaultTheme = createMuiTheme({
+ typography: {
+ suppressWarning: true,
+ },
+ });
return defaultTheme;
}
diff --git a/packages/material-ui/src/utils/deprecatedPropType.js b/packages/material-ui/src/utils/deprecatedPropType.js
new file mode 100644
index 00000000000000..01eef8ca8d7299
--- /dev/null
+++ b/packages/material-ui/src/utils/deprecatedPropType.js
@@ -0,0 +1,22 @@
+function deprecatedPropType(validator, reason) {
+ /* istanbul ignore if */
+ if (process.env.NODE_ENV === 'production') {
+ return () => null;
+ }
+
+ return (props, propName, componentName, location, propFullName) => {
+ const componentNameSafe = componentName || '<>';
+ const propFullNameSafe = propFullName || propName;
+
+ if (typeof props[propName] !== 'undefined') {
+ return new Error(
+ `The ${location} \`${propFullNameSafe}\` of ` +
+ `\`${componentNameSafe}\` is deprecated. ${reason}`,
+ );
+ }
+
+ return null;
+ };
+}
+
+export default deprecatedPropType;
diff --git a/packages/material-ui/src/utils/deprecatedPropType.test.js b/packages/material-ui/src/utils/deprecatedPropType.test.js
new file mode 100644
index 00000000000000..d9f69b456732d0
--- /dev/null
+++ b/packages/material-ui/src/utils/deprecatedPropType.test.js
@@ -0,0 +1,57 @@
+import { assert } from 'chai';
+import PropTypes from 'prop-types';
+import consoleErrorMock from 'test/utils/consoleErrorMock';
+import deprecatedPropType from './deprecatedPropType';
+
+describe('deprecatedPropType', () => {
+ const componentName = 'ComponentName';
+ const location = 'prop';
+
+ beforeEach(() => {
+ consoleErrorMock.spy();
+ });
+
+ afterEach(() => {
+ consoleErrorMock.reset();
+ });
+
+ it('should not warn', () => {
+ const propName = `children${new Date()}`;
+ const props = {};
+ PropTypes.checkPropTypes(
+ {
+ [propName]: deprecatedPropType(PropTypes.string, 'give me a reason'),
+ },
+ props,
+ location,
+ componentName,
+ );
+ assert.strictEqual(consoleErrorMock.callCount(), 0);
+ });
+
+ it('should warn once', () => {
+ const propName = `children${new Date()}`;
+ const props = {
+ [propName]: 'yolo',
+ };
+ PropTypes.checkPropTypes(
+ {
+ [propName]: deprecatedPropType(PropTypes.string, 'give me a reason'),
+ },
+ props,
+ location,
+ componentName,
+ );
+ assert.strictEqual(consoleErrorMock.callCount(), 1);
+ assert.match(consoleErrorMock.args()[0][0], /give me a reason/);
+ PropTypes.checkPropTypes(
+ {
+ [propName]: deprecatedPropType(PropTypes.string, 'give me a reason'),
+ },
+ props,
+ location,
+ componentName,
+ );
+ assert.strictEqual(consoleErrorMock.callCount(), 1);
+ });
+});
diff --git a/packages/material-ui/src/utils/reactHelpers.js b/packages/material-ui/src/utils/reactHelpers.js
index ab90ecd37f186a..bfdf759b9cc96e 100644
--- a/packages/material-ui/src/utils/reactHelpers.js
+++ b/packages/material-ui/src/utils/reactHelpers.js
@@ -1,5 +1,3 @@
-/* eslint-disable import/prefer-default-export */
-
import React from 'react';
import classNames from 'classnames';
diff --git a/packages/material-ui/src/withWidth/withWidth.js b/packages/material-ui/src/withWidth/withWidth.js
index 76e74d7827951b..2721e1ccb5fcb6 100644
--- a/packages/material-ui/src/withWidth/withWidth.js
+++ b/packages/material-ui/src/withWidth/withWidth.js
@@ -1,5 +1,3 @@
-/* eslint-disable react/no-did-mount-set-state */
-
import React from 'react';
import PropTypes from 'prop-types';
import EventListener from 'react-event-listener';
diff --git a/pages/_app.js b/pages/_app.js
index bb44aef5f7c2d2..dafa26935e3d9c 100644
--- a/pages/_app.js
+++ b/pages/_app.js
@@ -2,21 +2,15 @@ import 'docs/src/modules/components/bootstrap';
import React from 'react';
import App, { Container } from 'next/app';
-import url from 'url';
import find from 'lodash/find';
import { Provider } from 'react-redux';
import AppWrapper from 'docs/src/modules/components/AppWrapper';
import initRedux from 'docs/src/modules/redux/initRedux';
import findPages from /* preval */ 'docs/src/modules/utils/findPages';
import { loadCSS } from 'fg-loadcss/src/loadCSS';
-import acceptLanguage from 'accept-language';
import PageContext from 'docs/src/modules/components/PageContext';
-import { getCookie } from 'docs/src/modules/utils/helpers';
-import actionTypes from 'docs/src/modules/redux/actionTypes';
import getPageContext from 'docs/src/modules/styles/getPageContext';
-acceptLanguage.languages(['en', 'zh']);
-
if (process.browser) {
loadCSS(
'https://fonts.googleapis.com/icon?family=Material+Icons',
@@ -135,7 +129,7 @@ const pages = [
},
{
pathname: '/css-in-js',
- title: 'CSS in JS (experimental)',
+ title: 'CSS in JS (alpha)',
children: [
{
pathname: '/css-in-js/basics',
@@ -306,39 +300,8 @@ class MyApp extends App {
this.pageContext = getPageContext();
}
- state = {
- userLanguage: 'en',
- };
-
- componentDidMount() {
- const URL = url.parse(document.location.href, true);
- const userLanguage =
- acceptLanguage.get(URL.query.lang || getCookie('lang') || navigator.language) || 'en';
-
- if (this.state.userLanguage !== userLanguage) {
- this.setState({ userLanguage });
- }
-
- const paletteType = getCookie('paletteType');
- if (paletteType) {
- this.redux.dispatch({
- type: actionTypes.THEME_CHANGE_PALETTE_TYPE,
- payload: { paletteType },
- });
- }
-
- const paletteColors = getCookie('paletteColors');
- if (paletteColors) {
- this.redux.dispatch({
- type: actionTypes.THEME_CHANGE_PALETTE_COLORS,
- payload: { paletteColors: JSON.parse(paletteColors) },
- });
- }
- }
-
render() {
const { Component, pageProps, router } = this.props;
- const { userLanguage } = this.state;
let pathname = router.pathname;
if (pathname !== '/') {
@@ -356,17 +319,13 @@ class MyApp extends App {
return (
-
-
-
-
-
-
-
+
+
+
+
+
+
+
);
}
diff --git a/pages/_document.js b/pages/_document.js
index ec1cdb3a561840..7e7350b93ca4f4 100644
--- a/pages/_document.js
+++ b/pages/_document.js
@@ -24,7 +24,13 @@ const GOOGLE_ID = process.env.NODE_ENV === 'production' ? 'UA-106598593-2' : 'UA
class MyDocument extends Document {
render() {
- const { canonical, pageContext } = this.props;
+ const { canonical, pageContext, url } = this.props;
+
+ let font = 'https://fonts.googleapis.com/css?family=Roboto:300,400,500';
+
+ if (url.match(/onepirate/)) {
+ font = 'https://fonts.googleapis.com/css?family=Roboto+Condensed:700|Work+Sans:300,400';
+ }
return (
@@ -43,10 +49,7 @@ class MyDocument extends Document {
-
+
{/*
Preconnect allows the browser to setup early connections before an HTTP request
is actually sent to the server.
@@ -119,6 +122,7 @@ MyDocument.getInitialProps = async ctx => {
return {
...page,
pageContext,
+ url: ctx.req.url,
canonical: `https://material-ui.com${ctx.req.url.replace(/\/$/, '')}/`,
styles: (