Repository: codypearce/material-bread (js)

Issue:
DrawerBottom
**Describe your feature request**

"Bottom navigation drawers are modal drawers that are anchored to the bottom of the screen instead of the left or right edge. They are only used with bottom app bars.

These drawers open upon tapping the navigation menu icon in the bottom app bar. They are only for use on mobile."

**Context**
https://material.io/design/components/navigation-drawer.html#bottom-drawer
![image](https://user-images.githubusercontent.com/12564956/56465303-dbd35e80-63af-11e9-848d-2b98f1efdfa1.png)

- [ ] Create component
- [ ] Create storybook page
- [ ] Create doc page
- [ ] Create demos
- [ ] Create jest tests


Pull request:
62 drawer bottom
closes #62 

Patch:
diff --git a/docs/src/content/components/drawer-bottom/MainDemo.js b/docs/src/content/components/drawer-bottom/MainDemo.js
new file mode 100644
index 0000000..f7e1387
--- /dev/null
+++ b/docs/src/content/components/drawer-bottom/MainDemo.js
@@ -0,0 +1,68 @@
+import React from 'react';
+import { View } from 'react-native';
+import { ComponentMainDemo, CodeInline } from '@components';
+import {
+  DrawerBottom,
+  Button,
+  DrawerHeader,
+  DrawerSection,
+  DrawerItem,
+} from '../../../../../src/';
+
+export const code = `class MainDemo extends React.Component {
+  constructor(props) {
+    super(props)
+    this.state = {
+        visible: false,
+    }
+  }
+  render() {
+    return (
+      <View style={{ flex: 1}}>
+        <Button
+          text="Open Drawer"
+          onPress={() => {
+            this.setState({ visible: true });
+          }}
+        />
+        <DrawerBottom
+          visible={this.state.visible}
+          onBackdropPress={() => this.setState({ visible: false })}
+          onSwipeDown={() => this.setState({ visible: false })}>
+          <View>
+            <DrawerHeader title={'Jon Snow'} subtitle={'Knows nothing'} />
+            <DrawerSection>
+              <DrawerItem text={'Inbox'} icon={'mail'} active />
+              <DrawerItem text={'Outbox'} icon={'send'} />
+              <DrawerItem text={'Favorites'} icon={'favorite'} />
+            </DrawerSection>
+          </View>
+        </DrawerBottom>
+      </View>
+    );
+  }
+}`;
+
+const MainDemo = pageHref => (
+  <ComponentMainDemo
+    pageHref={pageHref}
+    description={
+      <div>
+        <CodeInline code="DrawerBottom" type="element" /> is a type of{' '}
+        <CodeInline code="SheetBottom" type="element" />, specifically the{' '}
+        <CodeInline code="modal" type="value" /> type. All the same props and
+        usages apply.
+      </div>
+    }
+    code={code}
+    scope={{
+      View,
+      DrawerBottom,
+      Button,
+      DrawerHeader,
+      DrawerSection,
+      DrawerItem,
+    }}
+  />
+);
+export default MainDemo;
diff --git a/docs/src/content/components/drawer-bottom/Props.js b/docs/src/content/components/drawer-bottom/Props.js
new file mode 100644
index 0000000..8a8c941
--- /dev/null
+++ b/docs/src/content/components/drawer-bottom/Props.js
@@ -0,0 +1,8 @@
+import React from 'react';
+import { ComponentProps } from '@components';
+import propData from './propData';
+
+const Props = pageHref => (
+  <ComponentProps pageHref={pageHref} propData={propData} />
+);
+export default Props;
diff --git a/docs/src/content/components/drawer-bottom/Usage.js b/docs/src/content/components/drawer-bottom/Usage.js
new file mode 100644
index 0000000..880761f
--- /dev/null
+++ b/docs/src/content/components/drawer-bottom/Usage.js
@@ -0,0 +1,42 @@
+import React from 'react';
+import { ComponentUsage } from '@components';
+
+export const code = `import React, { Component } from 'react';
+import { DrawerBottom, Button,  DrawerHeader, DrawerSection, DrawerItem, } from 'material-bread';
+
+class SelectPage extends React.Component {
+  constructor(props) {
+    super(props)
+    this.state = {
+        visible: false
+    }
+  }
+  render() {
+    return (
+      <View style={{ flex: 1}}>
+        <Button
+          text="Open Sheet"
+          onPress={() => {
+            this.setState({ visible: true });
+          }}
+        />
+        <DrawerBottom
+          visible={this.state.visible}
+          onBackdropPress={() => this.setState({ visible: false })}
+          onSwipeDown={() => this.setState({ visible: false })}>
+          <View>
+            <DrawerHeader title={'Jon Snow'} subtitle={'Knows nothing'} />
+            <DrawerSection bottomDivider>
+              <DrawerItem text={'Inbox'} icon={'mail'} active />
+              <DrawerItem text={'Outbox'} icon={'send'} />
+              <DrawerItem text={'Favorites'} icon={'favorite'} />
+            </DrawerSection>
+          </View>
+        </DrawerBottom>
+      </View>
+    );
+  }
+}`;
+
+const Usage = pageHref => <ComponentUsage pageHref={pageHref} code={code} />;
+export default Usage;
diff --git a/docs/src/content/components/drawer-bottom/index.js b/docs/src/content/components/drawer-bottom/index.js
new file mode 100644
index 0000000..49d239f
--- /dev/null
+++ b/docs/src/content/components/drawer-bottom/index.js
@@ -0,0 +1,30 @@
+import React, { Component } from 'react';
+import ComponentPageLayout from '../../../components/ComponentPage/ComponentPageLayout';
+import MainDemo from './MainDemo';
+import Usage from './Usage';
+import Props from './Props';
+
+const sections = [{ name: 'Component' }, { name: 'Usage' }, { name: 'Props' }];
+
+export default class DropdownPage extends Component {
+  render() {
+    return (
+      <div>
+        <ComponentPageLayout
+          componentName={'drawer-bottom'}
+          title={'DrawerBottom'}
+          description={`Bottom navigation drawers are modal drawers that are anchored to the bottom of the screen instead of the left or right edge.
+          `}
+          importCode={`import { DrawerBottom } from 'material-bread';`}
+          docsLink={
+            'https://material.io/design/components/navigation-drawer.html#bottom-drawer'
+          }
+          sideScrollMenuItems={sections}>
+          <MainDemo pageHref={'/components/drawer-bottom'} />
+          <Usage pageHref={'/components/drawer-bottom'} />
+          <Props pageHref={'/components/drawer-bottom'} />
+        </ComponentPageLayout>
+      </div>
+    );
+  }
+}
diff --git a/docs/src/content/components/drawer-bottom/propData.js b/docs/src/content/components/drawer-bottom/propData.js
new file mode 100644
index 0000000..8f780a5
--- /dev/null
+++ b/docs/src/content/components/drawer-bottom/propData.js
@@ -0,0 +1,36 @@
+import { createTableData } from '../../../utils/createPropData';
+const propData = [
+  [
+    'cardVerticalPadding',
+    'Padding vertical on the sheet, this used in calculating the position',
+    'number',
+    '20',
+  ],
+
+  ['duration', 'How long the hide animation takes', 'number', '200ms'],
+  [
+    'onBackdropPress',
+    'Callback when mask is pressed, usually to close sheet',
+    'func',
+    '',
+  ],
+  [
+    'onSwipeDown',
+    'Callback on swiping down, usually to close sheet',
+    'func',
+    '',
+  ],
+  ['onSwipeUp', 'Callback on swiping up', 'func', ''],
+  [
+    'pageHeight',
+    "Manually control the height of the page, useful for demos or sheets that shouldn't take up the whole page",
+    'number',
+    'ScreenHeight',
+  ],
+  ['style', 'Styles sheet element', 'object', ''],
+
+  ['visible', 'Toggles Sheet animation', 'bool', ''],
+  ['wrapperStyles', 'Styles wrapper around the whole page', 'object', ''],
+];
+
+export default createTableData(propData);
diff --git a/docs/src/navigation/componentPages.js b/docs/src/navigation/componentPages.js
index 13133cf..002f5c7 100644
--- a/docs/src/navigation/componentPages.js
+++ b/docs/src/navigation/componentPages.js
@@ -121,6 +121,10 @@ const components = [
     path: '/components/drawer-section',
     subsubItem: true,
   },
+  {
+    title: 'DrawerBottom',
+    path: '/components/drawer-bottom',
+  },
 
   {
     title: 'Fab',
diff --git a/docs/src/pages/components/drawer-bottom.js b/docs/src/pages/components/drawer-bottom.js
new file mode 100644
index 0000000..4df8f2a
--- /dev/null
+++ b/docs/src/pages/components/drawer-bottom.js
@@ -0,0 +1,1 @@
+export default from '../../content/components/drawer-bottom';
diff --git a/src/Components/DrawerBottom/DrawerBottom.js b/src/Components/DrawerBottom/DrawerBottom.js
new file mode 100644
index 0000000..536098c
--- /dev/null
+++ b/src/Components/DrawerBottom/DrawerBottom.js
@@ -0,0 +1,22 @@
+import React, { Component } from 'react';
+import PropTypes from 'prop-types';
+import withTheme from '../../Theme/withTheme';
+import { SheetBottom } from '../../';
+
+class DrawerBottom extends Component {
+  static propTypes = {
+    children: PropTypes.node,
+    style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
+  };
+  render() {
+    const { children, ...rest } = this.props;
+
+    return (
+      <SheetBottom type={'modal'} {...rest}>
+        {children}
+      </SheetBottom>
+    );
+  }
+}
+
+export default withTheme(DrawerBottom);
diff --git a/src/Components/DrawerBottom/DrawerBottom.stories.js b/src/Components/DrawerBottom/DrawerBottom.stories.js
new file mode 100644
index 0000000..4c6f0ae
--- /dev/null
+++ b/src/Components/DrawerBottom/DrawerBottom.stories.js
@@ -0,0 +1,52 @@
+import React from 'react';
+import { View, Platform } from 'react-native';
+
+import {
+  DrawerBottom,
+  Button,
+  DrawerItem,
+  DrawerHeader,
+  DrawerSection,
+} from '../../';
+import { State, Store } from '@sambego/storybook-state';
+import Header from '../../storybook/components/Header';
+import Container from '../../storybook/components/Container';
+import { storiesOf } from '../../storybook/helpers/storiesOf';
+
+const store = new Store({
+  visible: false,
+});
+
+export default storiesOf('Components|DrawerBottom', module)
+  .addParameters({ jest: ['DrawerBottom'] })
+  .add('Simple', () => (
+    <Container>
+      <Header title={'DrawerBottom'} />
+      <State store={store} style={{ flex: 1 }}>
+        {state => (
+          <View style={{ flex: 1, height: 300 }}>
+            <Button
+              text="Open Drawer"
+              onPress={() => {
+                store.set({ visible: true });
+              }}
+            />
+            <DrawerBottom
+              visible={state.visible}
+              pageHeight={Platform.OS == 'web' ? 400 : null}
+              onBackdropPress={() => store.set({ visible: false })}
+              onSwipeDown={() => store.set({ visible: false })}>
+              <View>
+                <DrawerHeader title={'Jon Snow'} subtitle={'Knows nothing'} />
+                <DrawerSection bottomDivider>
+                  <DrawerItem text={'Inbox'} icon={'mail'} active />
+                  <DrawerItem text={'Outbox'} icon={'send'} />
+                  <DrawerItem text={'Favorites'} icon={'favorite'} />
+                </DrawerSection>
+              </View>
+            </DrawerBottom>
+          </View>
+        )}
+      </State>
+    </Container>
+  ));
diff --git a/src/index.js b/src/index.js
index afc1c1e..b1c8430 100644
--- a/src/index.js
+++ b/src/index.js
@@ -55,6 +55,9 @@ export {
 export {
   default as DrawerSection,
 } from './Components/Drawer/DrawerSection/DrawerSection';
+export {
+  default as DrawerBottom,
+} from './Components/DrawerBottom/DrawerBottom';
 export { default as Divider } from './Components/Divider/Divider';
 export { default as Fab } from './Components/Fab/Fab';
 export {
diff --git a/src/storybook/stories/index.js b/src/storybook/stories/index.js
index 0f9f44f..72ec036 100644
--- a/src/storybook/stories/index.js
+++ b/src/storybook/stories/index.js
@@ -61,7 +61,9 @@ export {
 export {
   default as DrawerItem,
 } from '../../Components/Drawer/DrawerItem/DrawerItem.stories.js';
-
+export {
+  default as DrawerBottom,
+} from '../../Components/DrawerBottom/DrawerBottom.stories.js';
 export {
   default as Divider,
 } from '../../Components/Divider/Divider.stories.js';


Test patch:
diff --git a/src/Components/DrawerBottom/DrawerBottom.styles.js b/src/Components/DrawerBottom/DrawerBottom.styles.js
new file mode 100644
index 0000000..e69de29
diff --git a/src/Components/DrawerBottom/DrawerBottom.test.js b/src/Components/DrawerBottom/DrawerBottom.test.js
new file mode 100644
index 0000000..24524d7
--- /dev/null
+++ b/src/Components/DrawerBottom/DrawerBottom.test.js
@@ -0,0 +1,9 @@
+import React from 'react';
+import { DrawerBottom } from '../../..';
+
+import renderer from 'react-test-renderer';
+
+test('DrawerBottom Renders', () => {
+  const tree = renderer.create(<DrawerBottom />).toJSON();
+  expect(tree).toMatchSnapshot();
+});
diff --git a/src/Components/DrawerBottom/__snapshots__/DrawerBottom.test.js.snap b/src/Components/DrawerBottom/__snapshots__/DrawerBottom.test.js.snap
new file mode 100644
index 0000000..66b87db
--- /dev/null
+++ b/src/Components/DrawerBottom/__snapshots__/DrawerBottom.test.js.snap
@@ -0,0 +1,79 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`DrawerBottom Renders 1`] = `
+<Modal
+  animationType="none"
+  hardwareAccelerated={false}
+  onRequestClose={[Function]}
+  transparent={true}
+  visible={false}
+>
+  <View
+    style={
+      Array [
+        Object {
+          "backgroundColor": "#00000077",
+          "flex": 1,
+        },
+        undefined,
+      ]
+    }
+  >
+    <View
+      accessible={true}
+      isTVSelectable={true}
+      onResponderGrant={[Function]}
+      onResponderMove={[Function]}
+      onResponderRelease={[Function]}
+      onResponderTerminate={[Function]}
+      onResponderTerminationRequest={[Function]}
+      onStartShouldSetResponder={[Function]}
+      style={
+        Object {
+          "backgroundColor": "transparent",
+          "height": "100%",
+          "left": 0,
+          "opacity": 1,
+          "position": "absolute",
+          "top": 0,
+          "width": "100%",
+          "zIndex": -1,
+        }
+      }
+    />
+    <View
+      onMoveShouldSetResponder={[Function]}
+      onMoveShouldSetResponderCapture={[Function]}
+      onResponderEnd={[Function]}
+      onResponderGrant={[Function]}
+      onResponderMove={[Function]}
+      onResponderReject={[Function]}
+      onResponderRelease={[Function]}
+      onResponderStart={[Function]}
+      onResponderTerminate={[Function]}
+      onResponderTerminationRequest={[Function]}
+      onStartShouldSetResponder={[Function]}
+      onStartShouldSetResponderCapture={[Function]}
+      style={
+        Object {
+          "backgroundColor": "#fff",
+          "height": "100%",
+          "overflow": "hidden",
+          "paddingVertical": 20,
+          "transform": Array [
+            Object {
+              "translateY": 200,
+            },
+          ],
+          "width": "100%",
+          "zIndex": 1000,
+        }
+      }
+    >
+      <View
+        onLayout={[Function]}
+      />
+    </View>
+  </View>
+</Modal>
+`;