Repository: mlaursen/react-md
Pull request #98 (2016-06-03 20:03:35)

Navigation drawers
This will be the merge for completing #23 and #48 


Patch:
diff --git a/src/js/NavigationDrawers/NavigationDrawer.js b/src/js/NavigationDrawers/NavigationDrawer.js
index aab97bafb2..980bb9757f 100644
--- a/src/js/NavigationDrawers/NavigationDrawer.js
+++ b/src/js/NavigationDrawers/NavigationDrawer.js
@@ -1,49 +1,32 @@
 import React, { Component, PropTypes } from 'react';
 import PureRenderMixin from 'react-addons-pure-render-mixin';
+import CSSTransitionGroup from 'react-addons-css-transition-group';
 import classnames from 'classnames';
 
-import { setOverflow, isTouchDevice } from '../utils';
-import Divider from '../Dividers';
-import { List, ListItem } from '../Lists';
-import Subheader from '../Subheaders';
+import Drawer from './Drawer';
+import DrawerToolbar from './DrawerToolbar';
 import Overlay from '../Transitions/Overlay';
 
-import NavigationDrawerHeader from './NavigationDrawerHeader';
-import NavigationDrawerToolbar from './NavigationDrawerToolbar';
-
-const deprecate = (props, propName, component, to) => {
-  if(typeof props[propName] !== 'undefined') {
-    console.error(
-      `The prop '${propName}' has been renamed to '${to}' ` +
-      'for the next release. Check the upgrade docs at ' +
-      'https://github.com/mlaursen/react-md/tree/master/docs/UpgradeGuide.md ' +
-      'for more information.'
-    );
-  }
-};
-
-const removed = (props, propName) => {
-  if(typeof props[propName] !== 'undefined') {
-    console.error(
-      `The prop '${propName}' has been removed for the next release. Check ` +
-      'the upgrade docs at ' +
-      'https://github.com/mlaursen/react-md/tree/master/docs/UpgradeGuide.md ' +
-      'for more information.'
-    );
-  }
-};
-
 /**
- * Navigation drawers are an excellent component to use to set up the initial
- * layout of your application. This component combines a Navigation drawer
+ * Navigation Drawers are an excellent component to use to set up the initial
+ * layout of your application. This component combines a Drawer
  * (a sidebar of nav items), an app bar, and displays any additional content.
+ *
+ * The `NavigationDrawer` component is customizable to have different
+ * display states for mobile, tablet, and desktop displays.
  */
 export default class NavigationDrawer extends Component {
   constructor(props) {
     super(props);
 
     this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
-    this.state = { touch: false };
+    this.state = {
+      isOpen: typeof props.initiallyOpen !== 'undefined' ? props.initiallyOpen : true,
+      mobile: false,
+      tablet: false,
+      desktop: true,
+      drawerType: props.desktopDrawerType,
+    };
   }
 
   static DrawerType = {
@@ -59,36 +42,24 @@ export default class NavigationDrawer extends Component {
 
   static propTypes = {
     /**
-     * An optional style to apply to the navigation drawer itself.
+     * An optional style to apply to the entire container.
      */
-    style: (props, propName, component) => {
-      deprecate(props, propName, component, 'drawerStyle');
-      return PropTypes.object(props, propName, component);
-    },
+    style: PropTypes.object,
 
     /**
-     * An optional className to apply to the navigation drawer iteself.
+     * An optional className to apply to the entire container.
      */
-    className: (props, propName, component) => {
-      deprecate(props, propName, component, 'drawerClassName');
-      return PropTypes.string(props, propName, component);
-    },
+    className: PropTypes.string,
 
     /**
-     * An optional style to apply to the navigation drawer container.
+     * An optional style to apply to the drawer.
      */
-    containerStyle: (props, propName, component) => {
-      deprecate(props, propName, component, 'style');
-      return PropTypes.object(props, propName, component);
-    },
+    drawerStyle: PropTypes.object,
 
     /**
-     * An optional className to apply to the navigation drawer container.
+     * An optional className to apply to the drawer.
      */
-    containerClassName: (props, propName, component) => {
-      deprecate(props, propName, component, 'className');
-      return PropTypes.string(props, propName, component);
-    },
+    drawerClassName: PropTypes.string,
 
     /**
      * An optional style to apply to the main content.
@@ -111,26 +82,92 @@ export default class NavigationDrawer extends Component {
     toolbarClassName: PropTypes.string,
 
     /**
-     * Boolean if the navigation drawer is currently open.
+     * An optional title to display in the navigation drawer header.
      */
-    isOpen: (props, propName, component) => {
-      removed(props, propName);
-      return PropTypes.bool.isRequired(props, propName, component);
-    },
+    drawerTitle: PropTypes.string,
 
     /**
-     * An optional title to display in the navigation drawer header.
+     * Any additional children you want to display in the navigation drawer header after
+     * the optional title.
+     */
+    drawerChildren: PropTypes.node,
+
+    /**
+     * An optional title to display in the toolbar.
+     */
+    toolbarTitle: PropTypes.string,
+
+    /**
+     * Any additional children you want to display in the main toolbar after the menu
+     * button (for persistent drawers) and the optional title.
      */
-    title: (props, propName, component) => {
-      deprecate(props, propName, component, 'drawerTitle');
-      return PropTypes.string(props, propName, component);
-    },
+    toolbarChildren: PropTypes.node,
 
     /**
      * The main content to display.
      */
     children: PropTypes.node,
 
+    /**
+     * An optional boolean if the drawer is open for persistent or temporary
+     * drawers.
+     */
+    initiallyOpen: PropTypes.bool,
+
+    /**
+     * The `DrawerType` to use for mobile devices. If the `mobileMinWidth` prop
+     * matches or the `tabletMinWidth` prop matches and the orientation is portrait,
+     * this drawer type will be used.
+     */
+    mobileDrawerType: PropTypes.oneOf([
+      NavigationDrawer.DrawerType.TEMPORARY,
+      NavigationDrawer.DrawerType.TEMPORARY_MINI,
+    ]).isRequired,
+
+    /**
+     * The `DrawerType` to use for landscape oriented tablets. If the
+     * `tabletMinWidth` prop matches and the orientation is landscape, this drawer
+     * type will be used.
+     */
+    tabletDrawerType: PropTypes.oneOf([
+      NavigationDrawer.DrawerType.FULL_HEIGHT,
+      NavigationDrawer.DrawerType.CLIPPED,
+      NavigationDrawer.DrawerType.FLOATING,
+      NavigationDrawer.DrawerType.PERSISTENT,
+      NavigationDrawer.DrawerType.PERSISTENT_MINI,
+      NavigationDrawer.DrawerType.TEMPORARY,
+      NavigationDrawer.DrawerType.TEMPORARY_MINI,
+    ]).isRequired,
+
+    /**
+     * The `DrawerType` to use for desktop displays. If the `desktopMinWidth`
+     * prop matches, this drawer type will be used.
+     */
+    desktopDrawerType: PropTypes.oneOf([
+      NavigationDrawer.DrawerType.FULL_HEIGHT,
+      NavigationDrawer.DrawerType.CLIPPED,
+      NavigationDrawer.DrawerType.FLOATING,
+      NavigationDrawer.DrawerType.PERSISTENT,
+      NavigationDrawer.DrawerType.PERSISTENT_MINI,
+      NavigationDrawer.DrawerType.TEMPORARY,
+      NavigationDrawer.DrawerType.TEMPORARY_MINI,
+    ]).isRequired,
+
+    /**
+     * The min width to use for a mobile media query.
+     */
+    mobileMinWidth: PropTypes.number.isRequired,
+
+    /**
+     * The min width to use for a tablet media query.
+     */
+    tabletMinWidth: PropTypes.number.isRequired,
+
+    /**
+     * The min width to use for a desktop media query.
+     */
+    desktopMinWidth: PropTypes.number.isRequired,
+
     /**
      * The icon className to use for the main menu icon.
      */
@@ -152,31 +189,20 @@ export default class NavigationDrawer extends Component {
     closeIconChildren: PropTypes.node,
 
     /**
-     * A function to call that will open the navigation drawer. This will
-     * be applied to the main menu button.
+     * Boolean if the sliding drawer should automatically close when a nav item
+     * is clicked for a temporary or temporary mini drawer.
      */
-    openDrawer: (props, propName, component) => {
-      removed(props, propName);
-      return PropTypes.func(props, propName, component);
-    },
+    autoclose: PropTypes.bool.isRequired,
 
     /**
-     * A function to call that will close the navigation drawer. If the drawer
-     * is persistent or persistent mini, this function is required.
+     * An optional function to call when the sliding drawer's open state changes.
+     * This can happen from rotating a tablet, resizing the browser window,
+     * or toggling the drawer for persistent and temporary versions. The
+     * `onDrawerChange` prop will be given the new open state.
+     *
+     * `onDrawerChange(isOpen)`
      */
-    closeDrawer: (props, propName, component) => {
-      removed(props, propName);
-
-      const { PERSISTENT, PERSISTENT_MINI } = NavigationDrawer.DrawerType;
-      const { drawerType } = props;
-      if(drawerType !== PERSISTENT && drawerType !== PERSISTENT_MINI) {
-        return;
-      }
-
-      return PropTypes.func.isRequired(props, propName, component)
-        || PropTypes.string(props, 'closeIconClassName', component)
-        || PropTypes.node(props, 'closeIconChildren', component);
-    },
+    onDrawerChange: PropTypes.func,
 
     /**
      * A list of items to render in the navigation drawer. If an item
@@ -186,7 +212,17 @@ export default class NavigationDrawer extends Component {
      * ##### Additional Info
      */
     navItems: PropTypes.arrayOf(PropTypes.oneOfType([
-      PropTypes.node,
+      /**
+       * Any react component you want to render instead of using props
+       * to generate a component.
+       */
+      PropTypes.element,
+
+      /**
+       * An object of props to use that will generate either a `ListItem`, `Divider`,
+       * or a `Subheader` component. Any props not listed will be passed
+       * to the component.
+       */
       PropTypes.shape({
         /**
          * Boolean if this item should be rendered as a divider.
@@ -214,183 +250,267 @@ export default class NavigationDrawer extends Component {
     ])).isRequired,
 
     /**
-     * The drawer type to use.
+     * The transition name to use for the drawer sliding in and out of view.
      */
-    drawerType: (props, propName, component) => {
-      removed(props, propName);
-      return PropTypes.oneOf(Object.keys(NavigationDrawer.DrawerType).map(k => NavigationDrawer.DrawerType[k]))(props, propName, component);
-    },
+    transitionName: PropTypes.string.isRequired,
 
     /**
-     * Any additional children to display in the main toolbar.
+     * The transition enter duration for the drawer sliding in to view.
      */
-    toolbarChildren: PropTypes.node,
+    transitionEnterTimeout: PropTypes.number.isRequired,
 
     /**
-     * An optional title to display in the main toolbar.
+     * The transition leave duration for the drawer sliding out of view.
      */
-    toolbarTitle: PropTypes.string,
+    transitionLeaveTimeout: PropTypes.number.isRequired,
+
+    /**
+     * An optional transition to use when the content changes. To disable this transition,
+     * set the enter and leave timeouts to `0`, `null`, or `undefined`. Make sure your
+     * child has a new key as well.
+     */
+    contentTransitionName: PropTypes.string.isRequired,
 
     /**
-     * An optional header to display in the navigation drawer. This will be rendered
-     * before the `navItems`. This will replace the generated header if you gave the
-     * `title` prop.
+     * The transition enter timeout for when the main content changes. Settings this value
+     * to `0`, `null`, or `undefined` will disable the enter transition.
      */
-    navHeader: (props, propName, component) => {
-      removed(props, propName);
-      return PropTypes.node(props, propName, component);
-    },
+    contentTransitionEnterTimeout: PropTypes.number,
 
     /**
-     * Any additional children to display in the navigation drawer's header after the
-     * `title`.
+     * The transition leave timeout for when the main content changes. Settings this value
+     * to `0`, `null`, or `undefined` will disable the leave transition.
      */
-    navHeaderChildren: (props, propName, component) => {
-      deprecate(props, propName, component, 'drawerChildren');
-      return PropTypes.node(props, propName, component);
-    },
+    contentTransitionLeaveTimeout: PropTypes.number,
   };
 
   static defaultProps = {
-    drawerType: NavigationDrawer.DrawerType.FULL_HEIGHT,
+    mobileDrawerType: NavigationDrawer.DrawerType.TEMPORARY,
+    tabletDrawerType: NavigationDrawer.DrawerType.PERSISTENT,
+    desktopDrawerType: NavigationDrawer.DrawerType.FULL_HEIGHT,
+    mobileMinWidth: 320,
+    tabletMinWidth: 768,
+    desktopMinWidth: 1025,
     menuIconChildren: 'menu',
+    autoclose: true,
     closeIconChildren: 'keyboard_arrow_left',
+    transitionName: 'drawer',
+    transitionEnterTimeout: 300,
+    transitionLeaveTimeout: 300,
+    contentTransitionName: 'cross-fade',
+    contentTransitionEnterTimeout: 300,
   };
 
   componentDidMount() {
-    this.setState({ touch: isTouchDevice() }); // eslint-disable-line
+    window.addEventListener('resize', this._updateMedia);
+    this._updateMedia();
   }
 
-  componentWillUpdate(nextProps) {
-    if(nextProps.isOpen === this.props.isOpen && nextProps.drawerType === this.props.drawerType) {
-      return;
-    }
+  componentWillReceiveProps(nextProps) {
+    const {
+      mobileMinWidth,
+      mobileDrawerType,
+      tabletMinWidth,
+      tabletDrawerType,
+      desktopMinWidth,
+      desktopDrawerType,
+    } = this.props;
 
-    const temps = [
-      NavigationDrawer.DrawerType.TEMPORARY,
-      NavigationDrawer.DrawerType.TEMPORARY_MINI,
-    ];
+    if(nextProps.mobileMinWidth !== mobileMinWidth || nextProps.mobileDrawerType !== mobileDrawerType
+      || nextProps.tabletMinWidth !== tabletMinWidth || nextProps.tabletDrawerType !== tabletDrawerType
+      || nextProps.desktopMinWidth !== desktopMinWidth || nextProps.desktopDrawerType !== desktopDrawerType) {
+      this._updateDrawerType(nextProps);
+    }
+  }
 
-    const isNextTemp = temps.indexOf(nextProps.drawerType) !== -1;
-    const isCurrTemp = temps.indexOf(this.props.drawerType) !== -1;
-    if(!isNextTemp && !isCurrTemp) { return; }
-    if((isCurrTemp && !isNextTemp) || (!isCurrTemp && !isNextTemp)) {
-      setOverflow(false);
-    } else {
-      setOverflow(nextProps.isOpen);
+  componentWillUpdate(nextProps, nextState) {
+    if(this.state.isOpen !== nextState.isOpen && nextProps.onDrawerChange) {
+      nextProps.onDrawerChange(nextState.isOpen);
     }
   }
 
-  mapItemsToComponents = (mini, active, item, key) => {
-    if(React.isValidElement(item)) {
-      return item;
+  componentWillUnmount() {
+    window.removeEventListener('resize', this._updateMedia);
+  }
+
+  _matches = (min, max, orientation) => {
+    let media = 'only screen';
+    if(orientation) {
+      media += ` and (orientation: ${orientation})`;
+    }
+
+    media += ` and (min-width: ${min}px)`;
+
+    if(max) {
+      media += ` and (max-width: ${max}px)`;
     }
 
-    const { divider, subheader, primaryText, nestedItems, ...itemProps } = item;
-    let component;
-    if(divider) {
-      component = Divider;
-    } else if(subheader) {
-      component = Subheader;
+    return window.matchMedia(media).matches;
+  };
+
+  _updateDrawerType = (props) => {
+    const {
+      mobileMinWidth,
+      mobileDrawerType,
+      tabletMinWidth,
+      tabletDrawerType,
+      desktopMinWidth,
+      desktopDrawerType,
+    } = props;
+
+    const nextState = {
+      mobile: this._matches(mobileMinWidth, desktopMinWidth - 1),
+      tablet: this._matches(tabletMinWidth, desktopMinWidth - 1, 'landscape'),
+      desktop: this._matches(desktopMinWidth),
+    };
+
+    if(nextState.tablet) {
+      nextState.drawerType = tabletDrawerType;
+    } else if(nextState.mobile) {
+      nextState.drawerType = mobileDrawerType;
     } else {
-      component = ListItem;
+      nextState.drawerType = desktopDrawerType;
     }
 
-    const props = Object.assign({}, itemProps, {
-      key: item.key || key,
-      nestedItems: nestedItems && nestedItems.map(this.mapItemsToComponents.bind(this, mini, active)),
-    });
-    if(!mini || (mini && active)) {
-      props.primaryText = primaryText;
+    if(this._isFullHeight(nextState.drawerType) !== this._isFullHeight(this.state.drawerType)) {
+      nextState.isOpen = this._isFullHeight(nextState.drawerType);
     }
 
-    return React.createElement(component, props);
+    this.setState(nextState);
+  };
+
+  _updateMedia = () => {
+    this._updateDrawerType(this.props);
+  };
+
+  openDrawer = () => {
+    this.setState({ isOpen: true });
+  };
+
+  closeDrawer = () => {
+    this.setState({ isOpen: false });
+  };
+
+  _isFullHeight = (drawerType) => {
+    const { FULL_HEIGHT, CLIPPED, FLOATING } = NavigationDrawer.DrawerType;
+    return [FULL_HEIGHT, CLIPPED, FLOATING].indexOf(drawerType) !== -1;
+  };
+
+  _isPersistent = (drawerType) => {
+    const { PERSISTENT, PERSISTENT_MINI } = NavigationDrawer.DrawerType;
+    return [PERSISTENT, PERSISTENT_MINI].indexOf(drawerType) !== -1;
+  };
+
+  _isTemporary = (drawerType) => {
+    const { TEMPORARY, TEMPORARY_MINI } = NavigationDrawer.DrawerType;
+    return [TEMPORARY, TEMPORARY_MINI].indexOf(drawerType) !== -1;
+  };
+
+  _isMini = (drawerType) => {
+    const { PERSISTENT_MINI, TEMPORARY_MINI } = NavigationDrawer.DrawerType;
+    return [PERSISTENT_MINI, TEMPORARY_MINI].indexOf(drawerType) !== -1;
   };
 
   render() {
     const {
-      isOpen,
-      title,
-      toolbarTitle,
       style,
       className,
-      containerStyle,
-      containerClassName,
+      drawerStyle,
+      drawerClassName,
       contentStyle,
       contentClassName,
       toolbarStyle,
       toolbarClassName,
-      menuIconClassName,
+      drawerTitle,
+      drawerChildren,
       menuIconChildren,
-      children,
-      openDrawer,
-      closeDrawer,
-      closeIconClassName,
+      menuIconClassName,
       closeIconChildren,
-      navItems,
-      drawerType,
+      closeIconClassName,
+      toolbarTitle,
       toolbarChildren,
-      navHeader,
-      navHeaderChildren,
+      autoclose,
+      navItems,
+      children,
+      transitionName,
+      transitionEnterTimeout,
+      transitionLeaveTimeout,
+      contentTransitionName,
+      contentTransitionEnterTimeout,
+      contentTransitionLeaveTimeout,
     } = this.props;
 
-    const { touch } = this.state;
-    const { PERSISTENT, PERSISTENT_MINI, TEMPORARY, TEMPORARY_MINI } = NavigationDrawer.DrawerType;
-
-    const mini = drawerType === PERSISTENT_MINI || drawerType === TEMPORARY_MINI;
-    const persistent = drawerType === PERSISTENT_MINI || drawerType === PERSISTENT;
-    const temporary = drawerType === TEMPORARY || drawerType === TEMPORARY_MINI;
-    const active = isOpen || (!temporary && (!persistent && !touch));
-
-    let nav;
-    if(active || mini) {
-      const navigationItems = navItems.map(this.mapItemsToComponents.bind(this, mini, active));
-
-      nav = <List>{navigationItems}</List>;
-    }
-
-    let header;
-    if(navHeader) {
-      header = React.cloneElement(navHeader, { persistent });
-    } else if((active && mini) || !mini) {
-      header = (
-        <NavigationDrawerHeader
-          title={title}
-          closeDrawer={closeDrawer}
+    const { isOpen, drawerType } = this.state;
+    const persistent = this._isPersistent(drawerType);
+    const temporary = this._isTemporary(drawerType);
+    const mini = this._isMini(drawerType);
+
+    let drawer, overlay;
+    if(isOpen || mini) {
+      drawer = (
+        <Drawer
+          key="drawer"
+          style={drawerStyle}
+          className={drawerClassName}
+          autoclose={autoclose}
+          title={drawerTitle}
+          closeDrawer={this.closeDrawer}
           closeIconChildren={closeIconChildren}
           closeIconClassName={closeIconClassName}
+          temporary={temporary}
           persistent={persistent}
-          children={navHeaderChildren}
+          navItems={navItems}
+          drawerType={drawerType}
+          children={drawerChildren}
+          mini={mini}
+          isOpen={isOpen}
         />
       );
     }
 
-    const conditionalClassNames = { active };
+    if(temporary) {
+      overlay = <Overlay isOpen={isOpen} onClick={this.closeDrawer} />;
+    }
 
     return (
-      <div className={classnames('md-navigation-drawer-container', containerClassName, conditionalClassNames)} style={containerStyle}>
-        <nav className={classnames('md-navigation-drawer', className, drawerType, conditionalClassNames)} style={style}>
-          {header}
-          {nav}
-        </nav>
-        <div className={classnames('md-navigation-drawer-content', contentClassName, drawerType, conditionalClassNames)} style={contentStyle}>
-          <NavigationDrawerToolbar
-            className={classnames(toolbarClassName, drawerType, conditionalClassNames)}
-            temporary={temporary}
+      <CSSTransitionGroup
+        style={style}
+        className={classnames('md-navigation-drawer-container', className)}
+        component="div"
+        transitionName={transitionName}
+        transitionEnterTimeout={transitionEnterTimeout}
+        transitionLeaveTimeout={transitionLeaveTimeout}
+      >
+        {drawer}
+        <CSSTransitionGroup
+          style={contentStyle}
+          className={classnames('md-navigation-drawer-content', contentClassName, drawerType, {
+            'active': isOpen && !temporary,
+          })}
+          component="div"
+          transitionName={contentTransitionName}
+          transitionEnter={!!contentTransitionEnterTimeout}
+          transitionEnterTimeout={contentTransitionEnterTimeout}
+          transitionLeave={!!contentTransitionLeaveTimeout}
+          transitionLeaveTimeout={contentTransitionLeaveTimeout}
+        >
+          <DrawerToolbar
             isOpen={isOpen}
+            drawerType={drawerType}
+            style={toolbarStyle}
+            className={toolbarClassName}
+            temporary={temporary}
             persistent={persistent}
-            openDrawer={openDrawer}
-            menuIconClassName={menuIconClassName}
+            openDrawer={this.openDrawer}
             menuIconChildren={menuIconChildren}
+            menuIconClassName={menuIconClassName}
             title={toolbarTitle}
             children={toolbarChildren}
-            style={toolbarStyle}
-            touch={touch}
           />
           {children}
-          <Overlay isOpen={isOpen && (!persistent || persistent && touch)} onClick={closeDrawer} />
-        </div>
-      </div>
+          {overlay}
+        </CSSTransitionGroup>
+      </CSSTransitionGroup>
     );
   }
 }
diff --git a/src/js/NavigationDrawers/NavigationDrawerHeader.js b/src/js/NavigationDrawers/NavigationDrawerHeader.js
deleted file mode 100644
index 0b69203e2e..0000000000
--- a/src/js/NavigationDrawers/NavigationDrawerHeader.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import React, { PropTypes } from 'react';
-import classnames from 'classnames';
-
-import { IconButton } from '../Buttons';
-import Divider from '../Dividers';
-
-const NavigationDrawerHeader = (props) => {
-  const {
-    className,
-    children,
-    persistent,
-    title,
-    closeDrawer,
-    closeIconChildren,
-    closeIconClassName,
-  } = props;
-
-  let closeButton;
-  if(persistent && closeDrawer && (closeIconChildren || closeIconClassName)) {
-    closeButton = (
-      <IconButton
-        className="md-navigation-drawer-btn md-navigation-drawer-toggle"
-        onClick={closeDrawer}
-        iconClassName={closeIconClassName}
-        children={closeIconChildren}
-      />
-    );
-  }
-  return (
-    <header className={classnames('md-drawer-header', className)}>
-      {title && <h3 className="md-title">{title}</h3>}
-      {children}
-      {closeButton}
-      <Divider />
-    </header>
-  );
-};
-
-NavigationDrawerHeader.propTypes = {
-  className: PropTypes.string,
-  children: PropTypes.node,
-  persistent: PropTypes.bool.isRequired,
-  title: PropTypes.string,
-  closeDrawer: PropTypes.func,
-  closeIconClassName: PropTypes.string,
-  closeIconChildren: PropTypes.node,
-};
-
-export default NavigationDrawerHeader;
diff --git a/src/js/NavigationDrawers/NavigationDrawerToolbar.js b/src/js/NavigationDrawers/NavigationDrawerToolbar.js
deleted file mode 100644
index 7cf827af3c..0000000000
--- a/src/js/NavigationDrawers/NavigationDrawerToolbar.js
+++ /dev/null
@@ -1,65 +0,0 @@
-import React, { Component, PropTypes } from 'react';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
-import classnames from 'classnames';
-import IconButton from '../Buttons/IconButton';
-
-export default class NavigationDrawerToolbar extends Component {
-  constructor(props) {
-    super(props);
-
-    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
-  }
-
-  static propTypes = {
-    style: PropTypes.object,
-    className: PropTypes.string,
-    children: PropTypes.node,
-    title: PropTypes.string,
-    isOpen: PropTypes.bool,
-    temporary: PropTypes.bool,
-    persistent: PropTypes.bool,
-    openDrawer: PropTypes.func,
-    menuIconChildren: PropTypes.node,
-    menuIconClassName: PropTypes.string,
-    touch: PropTypes.bool,
-  };
-
-  render() {
-    const {
-      style,
-      className,
-      children,
-      title,
-      temporary,
-      isOpen,
-      persistent,
-      openDrawer,
-      touch,
-      menuIconChildren,
-      menuIconClassName,
-    } = this.props;
-
-    let menuBtn;
-    if(temporary || touch || (persistent && !isOpen)) {
-      menuBtn = (
-        <IconButton
-          key="toggle"
-          onClick={openDrawer}
-          children={menuIconChildren}
-          iconClassName={menuIconClassName}
-        />
-      );
-    }
-
-    return (
-      <header
-        className={classnames('md-navigation-drawer-toolbar', className)}
-        style={style}
-      >
-        {menuBtn}
-        {title && <h3 className="md-title">{title}</h3>}
-        {children}
-      </header>
-    );
-  }
-}
diff --git a/src/js/NavigationDrawers/_NavigationDrawer.js b/src/js/NavigationDrawers/_NavigationDrawer.js
deleted file mode 100644
index 980bb9757f..0000000000
--- a/src/js/NavigationDrawers/_NavigationDrawer.js
+++ /dev/null
@@ -1,516 +0,0 @@
-import React, { Component, PropTypes } from 'react';
-import PureRenderMixin from 'react-addons-pure-render-mixin';
-import CSSTransitionGroup from 'react-addons-css-transition-group';
-import classnames from 'classnames';
-
-import Drawer from './Drawer';
-import DrawerToolbar from './DrawerToolbar';
-import Overlay from '../Transitions/Overlay';
-
-/**
- * Navigation Drawers are an excellent component to use to set up the initial
- * layout of your application. This component combines a Drawer
- * (a sidebar of nav items), an app bar, and displays any additional content.
- *
- * The `NavigationDrawer` component is customizable to have different
- * display states for mobile, tablet, and desktop displays.
- */
-export default class NavigationDrawer extends Component {
-  constructor(props) {
-    super(props);
-
-    this.shouldComponentUpdate = PureRenderMixin.shouldComponentUpdate.bind(this);
-    this.state = {
-      isOpen: typeof props.initiallyOpen !== 'undefined' ? props.initiallyOpen : true,
-      mobile: false,
-      tablet: false,
-      desktop: true,
-      drawerType: props.desktopDrawerType,
-    };
-  }
-
-  static DrawerType = {
-    FULL_HEIGHT: 'full-height',
-    CLIPPED: 'clipped',
-    FLOATING: 'floating',
-    PERSISTENT: 'persistent',
-    PERSISTENT_MINI: 'mini',
-    TEMPORARY: 'temporary',
-    // want styles of temporary and mini. Little hacky.
-    TEMPORARY_MINI: 'temporary mini',
-  };
-
-  static propTypes = {
-    /**
-     * An optional style to apply to the entire container.
-     */
-    style: PropTypes.object,
-
-    /**
-     * An optional className to apply to the entire container.
-     */
-    className: PropTypes.string,
-
-    /**
-     * An optional style to apply to the drawer.
-     */
-    drawerStyle: PropTypes.object,
-
-    /**
-     * An optional className to apply to the drawer.
-     */
-    drawerClassName: PropTypes.string,
-
-    /**
-     * An optional style to apply to the main content.
-     */
-    contentStyle: PropTypes.object,
-
-    /**
-     * An optional className to apply to the main content.
-     */
-    contentClassName: PropTypes.string,
-
-    /**
-     * An optional style to apply to the main toolbar.
-     */
-    toolbarStyle: PropTypes.object,
-
-    /**
-     * An optional className to applt to the main toolbar.
-     */
-    toolbarClassName: PropTypes.string,
-
-    /**
-     * An optional title to display in the navigation drawer header.
-     */
-    drawerTitle: PropTypes.string,
-
-    /**
-     * Any additional children you want to display in the navigation drawer header after
-     * the optional title.
-     */
-    drawerChildren: PropTypes.node,
-
-    /**
-     * An optional title to display in the toolbar.
-     */
-    toolbarTitle: PropTypes.string,
-
-    /**
-     * Any additional children you want to display in the main toolbar after the menu
-     * button (for persistent drawers) and the optional title.
-     */
-    toolbarChildren: PropTypes.node,
-
-    /**
-     * The main content to display.
-     */
-    children: PropTypes.node,
-
-    /**
-     * An optional boolean if the drawer is open for persistent or temporary
-     * drawers.
-     */
-    initiallyOpen: PropTypes.bool,
-
-    /**
-     * The `DrawerType` to use for mobile devices. If the `mobileMinWidth` prop
-     * matches or the `tabletMinWidth` prop matches and the orientation is portrait,
-     * this drawer type will be used.
-     */
-    mobileDrawerType: PropTypes.oneOf([
-      NavigationDrawer.DrawerType.TEMPORARY,
-      NavigationDrawer.DrawerType.TEMPORARY_MINI,
-    ]).isRequired,
-
-    /**
-     * The `DrawerType` to use for landscape oriented tablets. If the
-     * `tabletMinWidth` prop matches and the orientation is landscape, this drawer
-     * type will be used.
-     */
-    tabletDrawerType: PropTypes.oneOf([
-      NavigationDrawer.DrawerType.FULL_HEIGHT,
-      NavigationDrawer.DrawerType.CLIPPED,
-      NavigationDrawer.DrawerType.FLOATING,
-      NavigationDrawer.DrawerType.PERSISTENT,
-      NavigationDrawer.DrawerType.PERSISTENT_MINI,
-      NavigationDrawer.DrawerType.TEMPORARY,
-      NavigationDrawer.DrawerType.TEMPORARY_MINI,
-    ]).isRequired,
-
-    /**
-     * The `DrawerType` to use for desktop displays. If the `desktopMinWidth`
-     * prop matches, this drawer type will be used.
-     */
-    desktopDrawerType: PropTypes.oneOf([
-      NavigationDrawer.DrawerType.FULL_HEIGHT,
-      NavigationDrawer.DrawerType.CLIPPED,
-      NavigationDrawer.DrawerType.FLOATING,
-      NavigationDrawer.DrawerType.PERSISTENT,
-      NavigationDrawer.DrawerType.PERSISTENT_MINI,
-      NavigationDrawer.DrawerType.TEMPORARY,
-      NavigationDrawer.DrawerType.TEMPORARY_MINI,
-    ]).isRequired,
-
-    /**
-     * The min width to use for a mobile media query.
-     */
-    mobileMinWidth: PropTypes.number.isRequired,
-
-    /**
-     * The min width to use for a tablet media query.
-     */
-    tabletMinWidth: PropTypes.number.isRequired,
-
-    /**
-     * The min width to use for a desktop media query.
-     */
-    desktopMinWidth: PropTypes.number.isRequired,
-
-    /**
-     * The icon className to use for the main menu icon.
-     */
-    menuIconClassName: PropTypes.string,
-
-    /**
-     * Any children required to render the main menu icon.
-     */
-    menuIconChildren: PropTypes.node,
-
-    /**
-     * The icon className to use for closing a persistent navigation drawer.
-     */
-    closeIconClassName: PropTypes.string,
-
-    /**
-     * Any children required to render the close icon for a persistent navigation drawer.
-     */
-    closeIconChildren: PropTypes.node,
-
-    /**
-     * Boolean if the sliding drawer should automatically close when a nav item
-     * is clicked for a temporary or temporary mini drawer.
-     */
-    autoclose: PropTypes.bool.isRequired,
-
-    /**
-     * An optional function to call when the sliding drawer's open state changes.
-     * This can happen from rotating a tablet, resizing the browser window,
-     * or toggling the drawer for persistent and temporary versions. The
-     * `onDrawerChange` prop will be given the new open state.
-     *
-     * `onDrawerChange(isOpen)`
-     */
-    onDrawerChange: PropTypes.func,
-
-    /**
-     * A list of items to render in the navigation drawer. If an item
-     * is a prop object, all props will be passed to either a `ListItem`,
-     * `Divider`, or `Subheader` component.
-     *
-     * ##### Additional Info
-     */
-    navItems: PropTypes.arrayOf(PropTypes.oneOfType([
-      /**
-       * Any react component you want to render instead of using props
-       * to generate a component.
-       */
-      PropTypes.element,
-
-      /**
-       * An object of props to use that will generate either a `ListItem`, `Divider`,
-       * or a `Subheader` component. Any props not listed will be passed
-       * to the component.
-       */
-      PropTypes.shape({
-        /**
-         * Boolean if this item should be rendered as a divider.
-         */
-        divider: PropTypes.bool,
-
-        /**
-         * Boolean if this item should be rendered as a subheader.
-         */
-        subheader: PropTypes.bool,
-
-        /**
-         * An optional component to render the `ListItem` as.
-         */
-        component: PropTypes.oneOfType([
-          PropTypes.string,
-          PropTypes.func,
-        ]),
-
-        /**
-         * The main text to be displayed in a `Subheader` or a `ListItem`.
-         */
-        primaryText: PropTypes.string,
-      }),
-    ])).isRequired,
-
-    /**
-     * The transition name to use for the drawer sliding in and out of view.
-     */
-    transitionName: PropTypes.string.isRequired,
-
-    /**
-     * The transition enter duration for the drawer sliding in to view.
-     */
-    transitionEnterTimeout: PropTypes.number.isRequired,
-
-    /**
-     * The transition leave duration for the drawer sliding out of view.
-     */
-    transitionLeaveTimeout: PropTypes.number.isRequired,
-
-    /**
-     * An optional transition to use when the content changes. To disable this transition,
-     * set the enter and leave timeouts to `0`, `null`, or `undefined`. Make sure your
-     * child has a new key as well.
-     */
-    contentTransitionName: PropTypes.string.isRequired,
-
-    /**
-     * The transition enter timeout for when the main content changes. Settings this value
-     * to `0`, `null`, or `undefined` will disable the enter transition.
-     */
-    contentTransitionEnterTimeout: PropTypes.number,
-
-    /**
-     * The transition leave timeout for when the main content changes. Settings this value
-     * to `0`, `null`, or `undefined` will disable the leave transition.
-     */
-    contentTransitionLeaveTimeout: PropTypes.number,
-  };
-
-  static defaultProps = {
-    mobileDrawerType: NavigationDrawer.DrawerType.TEMPORARY,
-    tabletDrawerType: NavigationDrawer.DrawerType.PERSISTENT,
-    desktopDrawerType: NavigationDrawer.DrawerType.FULL_HEIGHT,
-    mobileMinWidth: 320,
-    tabletMinWidth: 768,
-    desktopMinWidth: 1025,
-    menuIconChildren: 'menu',
-    autoclose: true,
-    closeIconChildren: 'keyboard_arrow_left',
-    transitionName: 'drawer',
-    transitionEnterTimeout: 300,
-    transitionLeaveTimeout: 300,
-    contentTransitionName: 'cross-fade',
-    contentTransitionEnterTimeout: 300,
-  };
-
-  componentDidMount() {
-    window.addEventListener('resize', this._updateMedia);
-    this._updateMedia();
-  }
-
-  componentWillReceiveProps(nextProps) {
-    const {
-      mobileMinWidth,
-      mobileDrawerType,
-      tabletMinWidth,
-      tabletDrawerType,
-      desktopMinWidth,
-      desktopDrawerType,
-    } = this.props;
-
-    if(nextProps.mobileMinWidth !== mobileMinWidth || nextProps.mobileDrawerType !== mobileDrawerType
-      || nextProps.tabletMinWidth !== tabletMinWidth || nextProps.tabletDrawerType !== tabletDrawerType
-      || nextProps.desktopMinWidth !== desktopMinWidth || nextProps.desktopDrawerType !== desktopDrawerType) {
-      this._updateDrawerType(nextProps);
-    }
-  }
-
-  componentWillUpdate(nextProps, nextState) {
-    if(this.state.isOpen !== nextState.isOpen && nextProps.onDrawerChange) {
-      nextProps.onDrawerChange(nextState.isOpen);
-    }
-  }
-
-  componentWillUnmount() {
-    window.removeEventListener('resize', this._updateMedia);
-  }
-
-  _matches = (min, max, orientation) => {
-    let media = 'only screen';
-    if(orientation) {
-      media += ` and (orientation: ${orientation})`;
-    }
-
-    media += ` and (min-width: ${min}px)`;
-
-    if(max) {
-      media += ` and (max-width: ${max}px)`;
-    }
-
-    return window.matchMedia(media).matches;
-  };
-
-  _updateDrawerType = (props) => {
-    const {
-      mobileMinWidth,
-      mobileDrawerType,
-      tabletMinWidth,
-      tabletDrawerType,
-      desktopMinWidth,
-      desktopDrawerType,
-    } = props;
-
-    const nextState = {
-      mobile: this._matches(mobileMinWidth, desktopMinWidth - 1),
-      tablet: this._matches(tabletMinWidth, desktopMinWidth - 1, 'landscape'),
-      desktop: this._matches(desktopMinWidth),
-    };
-
-    if(nextState.tablet) {
-      nextState.drawerType = tabletDrawerType;
-    } else if(nextState.mobile) {
-      nextState.drawerType = mobileDrawerType;
-    } else {
-      nextState.drawerType = desktopDrawerType;
-    }
-
-    if(this._isFullHeight(nextState.drawerType) !== this._isFullHeight(this.state.drawerType)) {
-      nextState.isOpen = this._isFullHeight(nextState.drawerType);
-    }
-
-    this.setState(nextState);
-  };
-
-  _updateMedia = () => {
-    this._updateDrawerType(this.props);
-  };
-
-  openDrawer = () => {
-    this.setState({ isOpen: true });
-  };
-
-  closeDrawer = () => {
-    this.setState({ isOpen: false });
-  };
-
-  _isFullHeight = (drawerType) => {
-    const { FULL_HEIGHT, CLIPPED, FLOATING } = NavigationDrawer.DrawerType;
-    return [FULL_HEIGHT, CLIPPED, FLOATING].indexOf(drawerType) !== -1;
-  };
-
-  _isPersistent = (drawerType) => {
-    const { PERSISTENT, PERSISTENT_MINI } = NavigationDrawer.DrawerType;
-    return [PERSISTENT, PERSISTENT_MINI].indexOf(drawerType) !== -1;
-  };
-
-  _isTemporary = (drawerType) => {
-    const { TEMPORARY, TEMPORARY_MINI } = NavigationDrawer.DrawerType;
-    return [TEMPORARY, TEMPORARY_MINI].indexOf(drawerType) !== -1;
-  };
-
-  _isMini = (drawerType) => {
-    const { PERSISTENT_MINI, TEMPORARY_MINI } = NavigationDrawer.DrawerType;
-    return [PERSISTENT_MINI, TEMPORARY_MINI].indexOf(drawerType) !== -1;
-  };
-
-  render() {
-    const {
-      style,
-      className,
-      drawerStyle,
-      drawerClassName,
-      contentStyle,
-      contentClassName,
-      toolbarStyle,
-      toolbarClassName,
-      drawerTitle,
-      drawerChildren,
-      menuIconChildren,
-      menuIconClassName,
-      closeIconChildren,
-      closeIconClassName,
-      toolbarTitle,
-      toolbarChildren,
-      autoclose,
-      navItems,
-      children,
-      transitionName,
-      transitionEnterTimeout,
-      transitionLeaveTimeout,
-      contentTransitionName,
-      contentTransitionEnterTimeout,
-      contentTransitionLeaveTimeout,
-    } = this.props;
-
-    const { isOpen, drawerType } = this.state;
-    const persistent = this._isPersistent(drawerType);
-    const temporary = this._isTemporary(drawerType);
-    const mini = this._isMini(drawerType);
-
-    let drawer, overlay;
-    if(isOpen || mini) {
-      drawer = (
-        <Drawer
-          key="drawer"
-          style={drawerStyle}
-          className={drawerClassName}
-          autoclose={autoclose}
-          title={drawerTitle}
-          closeDrawer={this.closeDrawer}
-          closeIconChildren={closeIconChildren}
-          closeIconClassName={closeIconClassName}
-          temporary={temporary}
-          persistent={persistent}
-          navItems={navItems}
-          drawerType={drawerType}
-          children={drawerChildren}
-          mini={mini}
-          isOpen={isOpen}
-        />
-      );
-    }
-
-    if(temporary) {
-      overlay = <Overlay isOpen={isOpen} onClick={this.closeDrawer} />;
-    }
-
-    return (
-      <CSSTransitionGroup
-        style={style}
-        className={classnames('md-navigation-drawer-container', className)}
-        component="div"
-        transitionName={transitionName}
-        transitionEnterTimeout={transitionEnterTimeout}
-        transitionLeaveTimeout={transitionLeaveTimeout}
-      >
-        {drawer}
-        <CSSTransitionGroup
-          style={contentStyle}
-          className={classnames('md-navigation-drawer-content', contentClassName, drawerType, {
-            'active': isOpen && !temporary,
-          })}
-          component="div"
-          transitionName={contentTransitionName}
-          transitionEnter={!!contentTransitionEnterTimeout}
-          transitionEnterTimeout={contentTransitionEnterTimeout}
-          transitionLeave={!!contentTransitionLeaveTimeout}
-          transitionLeaveTimeout={contentTransitionLeaveTimeout}
-        >
-          <DrawerToolbar
-            isOpen={isOpen}
-            drawerType={drawerType}
-            style={toolbarStyle}
-            className={toolbarClassName}
-            temporary={temporary}
-            persistent={persistent}
-            openDrawer={this.openDrawer}
-            menuIconChildren={menuIconChildren}
-            menuIconClassName={menuIconClassName}
-            title={toolbarTitle}
-            children={toolbarChildren}
-          />
-          {children}
-          {overlay}
-        </CSSTransitionGroup>
-      </CSSTransitionGroup>
-    );
-  }
-}
diff --git a/src/scss/_media-queries.scss b/src/scss/_media-queries.scss
index 6419acf966..885d06b794 100644
--- a/src/scss/_media-queries.scss
+++ b/src/scss/_media-queries.scss
@@ -48,3 +48,7 @@ $md-mobile-max-width: $md-desktop-min-width - 1;
     @include md-pickers-landscape();
   }
 }
+
+@media only screen and (orientation: landscape) and (min-width: #{$md-tablet-min-width}) and (max-width: #{$md-mobile-max-width}) {
+  @include md-navigation-drawer-tablet-landscape();
+}
diff --git a/src/scss/components/_navigation-drawers.scss b/src/scss/components/_navigation-drawers.scss
index 9278866e50..27c15337f8 100644
--- a/src/scss/components/_navigation-drawers.scss
+++ b/src/scss/components/_navigation-drawers.scss
@@ -2,24 +2,30 @@
   @include md-box-shadow(1);
 
   height: 100vh;
+  left: 0;
   overflow-y: auto;
   position: fixed;
-  transition-duration: $md-transition-time;
-  transition-property: box-shadow, left, width;
   z-index: $md-navigation-drawer-z-index;
 
   &.active,
   &.mini {
-    left: 0;
     z-index: $md-navigation-drawer-z-index - 5;
   }
 
-  &.mini:not(.active) {
+  &.mini {
+    overflow-x: hidden;
+    transition-duration: $md-navigation-drawer-transition-time;
+    transition-property: width;
     width: $md-navigation-drawer-mini-width;
 
     .md-list-tile {
       justify-content: center;
     }
+
+    &:not(.active) .md-tile-content {
+      display: none;
+      visibility: hidden;
+    }
   }
 
   &.temporary.active {
@@ -30,6 +36,9 @@
 }
 
 .md-navigation-drawer-content {
+  padding-left: 0;
+  transition: padding-left $md-navigation-drawer-transition-time;
+
   &.mini {
     padding-left: $md-navigation-drawer-mini-width;
     position: relative;
@@ -64,7 +73,7 @@
   left: 0;
   position: fixed;
   top: 0;
-  transition-duration: $md-transition-time;
+  transition-duration: $md-navigation-drawer-transition-time;
   transition-property: box-shadow, left, width;
   width: 100%;
   z-index: $md-navigation-drawer-toolbar-z-index;
@@ -91,3 +100,26 @@
 .md-navigation-drawer-toggle {
   float: right;
 }
+
+
+.drawer-leave,
+.drawer-enter.drawer-enter-active {
+  left: 0;
+}
+
+.drawer-enter-active,
+.drawer-leave-active {
+  transition: left $md-navigation-drawer-transition-time;
+}
+
+.cross-fade-enter {
+  opacity: .01;
+  transform: translate3d(0, $md-cross-fade-distance, 0);
+
+  &.cross-fade-enter-active {
+    opacity: 1;
+    transform: translate3d(0, 0, 0) $md-cross-fade-transition-time;
+    transition-duration: $md-cross-fade-transition-time;
+    transition-property: transform, opacity;
+  }
+}
diff --git a/src/scss/helpers/_variables.scss b/src/scss/helpers/_variables.scss
index 280a43f78c..706dd44fab 100644
--- a/src/scss/helpers/_variables.scss
+++ b/src/scss/helpers/_variables.scss
@@ -6,6 +6,12 @@
 /// @group media-queries
 $md-mobile-min-width: 320px !default;
 
+/// The min width for the tablet media query.
+///
+/// @type String
+/// @group media-queries
+$md-tablet-min-width: 768px !default;
+
 
 /// The min width for the desktop media query.
 ///
@@ -796,6 +802,67 @@ $md-toolbar-title-keyline: 72px;
 
 
 
+// ===================================================================================
+// Navigation Drawers
+// ===================================================================================
+
+/// The width for the navigation drawer on mobile devices.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-mobile-left: calc(-100% - 56px) !default;
+
+/// The width for the navigation drawer on mobile devices.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-mobile-width: calc(100% - 56px) !default;
+
+/// The width for the navigation drawer on desktops.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-desktop-width: 256px !default;
+
+/// The width for a mini navigation drawer that has not been opened.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-mini-width: 72px !default;
+
+/// The max width for a navigation drawer on mobile devices.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-mobile-max-width: 320px;
+
+/// the max width for a navigation drawer on desktop devices.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-desktop-max-width: 400px;
+
+
+/// The transition time for the navigation drawer to slide in and out of view.
+///
+/// @type String
+/// @group navigation-drawers
+$md-navigation-drawer-transition-time: $md-transition-time * 2 !default;
+
+/// The distance that the cross-fade transition should move.
+///
+/// @type String
+/// @group navigation-drawers
+$md-cross-fade-distance: 10px !default;
+
+/// The transition time for the cross-fade transition.
+///
+/// @type Time
+/// @group navigation-drawers
+$md-cross-fade-transition-time: $md-transition-time * 2 !default;
+
+
+
 // ===================================================================================
 // Sidebars
 // ===================================================================================
@@ -810,13 +877,15 @@ $md-sidebar-open-width: 256px !default;
 ///
 /// @type String
 /// @group sidebars
-$md-sidebar-mobile-max-width: 320px;
+/// @alias md-navigation-drawer-mobile-max-width
+$md-sidebar-mobile-max-width: $md-navigation-drawer-mobile-max-width;
 
 /// The max open width for the sidebar that is docked to the left.
 ///
 /// @type String
 /// @group sidebars
-$md-sidebar-desktop-max-width: 400px;
+/// @alias md-navigation-drawer-desktop-max-width
+$md-sidebar-desktop-max-width: $md-navigation-drawer-desktop-max-width;
 
 
 
@@ -855,7 +924,7 @@ $md-snackbar-lr-padding: 24px;
 /// @group snackbars
 $md-snackbar-tb-padding: 14px;
 
-/// The margin left for the action button in a sidebar.
+/// The margin left for the action button in a snackbar.
 ///
 /// @type String
 /// @group snackbars
@@ -1569,37 +1638,6 @@ $md-tooltip-desktop-lr-padding: 8px;
 $md-tooltip-desktop-height: 22px;
 
 
-
-// ===================================================================================
-// Navigation Drawers
-// ===================================================================================
-
-/// The width for the navigation drawer on mobile devices.
-///
-/// @type String
-/// @group navigation-drawers
-$md-navigation-drawer-mobile-left: calc(-100% - 56px) !default;
-
-/// The width for the navigation drawer on mobile devices.
-///
-/// @type String
-/// @group navigation-drawers
-$md-navigation-drawer-mobile-width: calc(100% - 56px) !default;
-
-/// The width for the navigation drawer on desktops.
-///
-/// @type String
-/// @group navigation-drawers
-$md-navigation-drawer-desktop-width: 256px !default;
-
-///
-///
-/// @type String
-/// @group navigation-drawers
-$md-navigation-drawer-mini-width: 72px !default;
-
-
-
 // ===================================================================================
 // Data Tables
 // ===================================================================================
diff --git a/src/scss/helpers/mixins/_navigation-drawers.scss b/src/scss/helpers/mixins/_navigation-drawers.scss
index 2db831ce4e..16b550777b 100644
--- a/src/scss/helpers/mixins/_navigation-drawers.scss
+++ b/src/scss/helpers/mixins/_navigation-drawers.scss
@@ -1,9 +1,31 @@
+/// Styles the navigation drawer component to use a specific toolbar height at a
+/// media screen size.
+///
+/// @access private
+/// @param {String} $height - The current height of the toolbar for this media size
+/// @group navigation-drawers
 @mixin md-navigation-drawer-media($height) {
-  .md-navigation-drawer-content,
-  .md-navigation-drawer.mini:not(.active) {
+  .md-navigation-drawer-content {
     padding-top: $height;
   }
 
+  .md-navigation-drawer {
+    &.clipped,
+    &.floating,
+    &.mini:not(.active) {
+      max-height: calc(100vh - #{$height});
+    }
+
+    &.mini:not(.active) {
+      top: $height;
+    }
+
+    &.clipped,
+    &.floating {
+      margin-top: $height;
+    }
+  }
+
   .md-drawer-header,
   .md-navigation-drawer-toolbar {
     height: $height;
@@ -19,34 +41,59 @@
   }
 }
 
+/// Styles the navigation drawer component for a portait mobile device
+///
+/// @group navigation-drawers
 @mixin md-navigation-drawer-mobile-portrait() {
   @include md-navigation-drawer-media($md-toolbar-mobile-portrait-height);
 }
 
+/// Styles the navigation drawer component for a landscape mobile device
+///
+/// @group navigation-drawers
 @mixin md-navigation-drawer-mobile-landscape() {
   @include md-navigation-drawer-media($md-toolbar-mobile-landscape-height);
 }
 
+/// Styles the navigation drawer component for a landscape tablet device
+///
+/// @group navigation-drawers
+@mixin md-navigation-drawer-tablet-landscape() {
+  @include md-navigation-drawer-landscape-devices();
+}
+
+/// Styles the navigation drawer component for a mobile device. This updates
+/// the drawer so it will always be temporary and updates the width.
+///
+/// @group navigation-drawers
 @mixin md-navigation-drawer-mobile() {
   .md-navigation-drawer {
-    left: $md-navigation-drawer-mobile-left;
-    max-width: $md-sidebar-mobile-max-width;
-    width: $md-navigation-drawer-mobile-width;
+    @include md-box-shadow(3);
 
-    &.active {
-      @include md-box-shadow(3);
+    max-width: $md-navigation-drawer-mobile-max-width;
+    width: $md-navigation-drawer-mobile-width;
+    z-index: $md-navigation-drawer-temporary-z-index;
 
-      z-index: $md-navigation-drawer-temporary-z-index;
+    &.mini.active {
+      width: $md-navigation-drawer-mobile-width;
     }
   }
+
+  .drawer-enter,
+  .drawer-leave.drawer-leave-active {
+    left: $md-navigation-drawer-mobile-left;
+  }
 }
 
-@mixin md-navigation-drawer-desktop() {
-  @include md-navigation-drawer-media($md-toolbar-desktop-height);
 
+/// Styles the navigation drawer component for larger screened landscape
+/// devices. This should be used for landscape tablets and desktop screens.
+///
+/// @access private
+/// @group navigation-drawers
+@mixin md-navigation-drawer-landscape-devices() {
   .md-navigation-drawer {
-    left: -$md-navigation-drawer-desktop-width;
-    max-width: $md-sidebar-desktop-max-width;
+    max-width: $md-navigation-drawer-desktop-max-width;
     width: $md-navigation-drawer-desktop-width;
 
     &.floating {
@@ -55,10 +102,12 @@
 
     &.floating,
     &.clipped {
-      height: calc(100vh - #{$md-toolbar-desktop-height});
-      margin-top: $md-toolbar-desktop-height;
       z-index: $md-navigation-drawer-z-index - 5;
     }
+
+    &.mini.active {
+      width: $md-navigation-drawer-desktop-width;
+    }
   }
 
   .md-navigation-drawer-toolbar {
@@ -70,16 +119,25 @@
     }
   }
 
-  .md-navigation-drawer-content {
-    &:not(.temporary) {
-      &.active {
-        padding-left: $md-navigation-drawer-desktop-width;
-        width: 100%;
-      }
-    }
+  .md-navigation-drawer-content.active {
+    padding-left: $md-navigation-drawer-desktop-width;
+    width: 100%;
+  }
+
+  .drawer-enter,
+  .drawer-leave.drawer-leave-active {
+    left: -$md-navigation-drawer-desktop-width;
   }
 }
 
+/// Styles the navigation drawer component for desktop displays.
+///
+/// @group navigation-drawers
+@mixin md-navigation-drawer-desktop() {
+  @include md-navigation-drawer-media($md-toolbar-desktop-height);
+  @include md-navigation-drawer-landscape-devices();
+}
+
 /// Themes the navigation drawers with the given color
 ///
 /// @example scss - Basic Usage SASS


Test patch:
diff --git a/src/js/NavigationDrawers/__tests__/NavigationDrawer.js b/src/js/NavigationDrawers/__tests__/NavigationDrawer.js
index a766fdf17b..d1b1ca1a91 100644
--- a/src/js/NavigationDrawers/__tests__/NavigationDrawer.js
+++ b/src/js/NavigationDrawers/__tests__/NavigationDrawer.js
@@ -1,5 +1,5 @@
 /*eslint-env jest*/
-jest.unmock('../_NavigationDrawer');
+jest.unmock('../NavigationDrawer');
 
 import React from 'react';
 import CSSTransitionGroup from 'react-addons-css-transition-group';
@@ -10,7 +10,7 @@ import {
   scryRenderedComponentsWithType,
 } from 'react-addons-test-utils';
 
-import NavigationDrawer from '../_NavigationDrawer';
+import NavigationDrawer from '../NavigationDrawer';
 import Drawer from '../Drawer';
 import DrawerToolbar from '../DrawerToolbar';
 import Overlay from '../../Transitions/Overlay';