Repository: callstack/react-native-paper
Pull request #835 (2019-02-11 14:54:05)

fix: limit Dialog.ScrollArea height
<!-- Please provide enough information so that others can review your pull request. -->
<!-- Keep pull requests small and focused on a single change. -->

### Motivation

If you don't set `maxHeight` on the `DialogScrollArea` itself but instead on `Dialog` (which is mostly what you want) the content of the dialog takes too much space and the actions won't be visible anymore:
![image](https://user-images.githubusercontent.com/5358638/52570995-071c5900-2e15-11e9-9959-84c7c3eb73a0.png)

### Test plan

Open the example for the dialog with long text:
![image](https://user-images.githubusercontent.com/5358638/52571041-1ac7bf80-2e15-11e9-8643-a9afa9e66d14.png)

Tests on iDevice and web.


Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 2f194fe02..2e9c2fc2c 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -47,7 +47,7 @@ jobs:
             at: ~/react-native-paper
         - run: |
             yarn lint
-            yarn flow
+            yarn flow check
   typescript:
       <<: *defaults
       steps:
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
index 88877072e..cd58d1a6f 100644
--- a/CODE_OF_CONDUCT.md
+++ b/CODE_OF_CONDUCT.md
@@ -1,73 +1,3 @@
-# Contributor Covenant Code of Conduct
+# Code of Conduct
 
-## Our Pledge
-
-In the interest of fostering an open and welcoming environment, we as
-contributors and maintainers pledge to making participation in our project and
-our community a harassment-free experience for everyone, regardless of age, body
-size, disability, ethnicity, gender identity and expression, level of experience,
-nationality, personal appearance, race, religion, or sexual identity and
-orientation.
-
-## Our Standards
-
-Examples of behavior that contributes to creating a positive environment
-include:
-
-* Using welcoming and inclusive language
-* Being respectful of differing viewpoints and experiences
-* Gracefully accepting constructive criticism
-* Focusing on what is best for the community
-* Showing empathy towards other community members
-
-Examples of unacceptable behavior by participants include:
-
-* The use of sexualized language or imagery and unwelcome sexual attention or
-  advances
-* Trolling, insulting/derogatory comments, and personal or political attacks
-* Public or private harassment
-* Publishing others' private information, such as a physical or electronic
-  address, without explicit permission
-* Other conduct which could reasonably be considered inappropriate in a
-  professional setting
-
-## Our Responsibilities
-
-Project maintainers are responsible for clarifying the standards of acceptable
-behavior and are expected to take appropriate and fair corrective action in
-response to any instances of unacceptable behavior.
-
-Project maintainers have the right and responsibility to remove, edit, or
-reject comments, commits, code, wiki edits, issues, and other contributions
-that are not aligned to this Code of Conduct, or to ban temporarily or
-permanently any contributor for other behaviors that they deem inappropriate,
-threatening, offensive, or harmful.
-
-## Scope
-
-This Code of Conduct applies both within project spaces and in public spaces
-when an individual is representing the project or its community. Examples of
-representing a project or community include using an official project e-mail
-address, posting via an official social media account, or acting as an appointed
-representative at an online or offline event. Representation of a project may be
-further defined and clarified by project maintainers.
-
-## Enforcement
-
-Instances of abusive, harassing, or otherwise unacceptable behavior may be
-reported by contacting the project team at [oss@callstack.io](mailto:oss@callstack.io). All
-complaints will be reviewed and investigated and will result in a response that
-is deemed necessary and appropriate to the circumstances. The project team is
-obligated to maintain confidentiality with regard to the reporter of an incident.
-Further details of specific enforcement policies may be posted separately.
-
-Project maintainers who do not follow or enforce the Code of Conduct in good
-faith may face temporary or permanent repercussions as determined by other
-members of the project's leadership.
-
-## Attribution
-
-This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
-available at https://www.contributor-covenant.org/version/1/4/code-of-conduct/
-
-[homepage]: https://www.contributor-covenant.org
+Callstack has adopted a Code of Conduct that we expect project participants to adhere to. Please read the [full text](https://callstack.com/code-of-conduct/?utm_source=github.com&utm_medium=referral&utm_campaign=react-native-paper&utm_term=code-of-conduct) so that you can understand what actions will and will not be tolerated.
diff --git a/README.md b/README.md
index 3eca287a7..1290229e1 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@
 [![All Contributors][all-contributors-badge]][all-contributors]
 [![PRs Welcome][prs-welcome-badge]][prs-welcome]
 [![Chat][chat-badge]][chat]
+[![Sponsored by Callstack][callstack-badge]][callstack]
 
 ## Features
 
@@ -40,6 +41,10 @@ Check the components and their usage in our [documentation](https://callstack.gi
 
 Read the [contribution guidelines](/CONTRIBUTING.md) before contributing.
 
+## Made with ❤️ at Callstack
+
+`react-native-paper` is an open source project and will always remain free to use. If you think it's cool, please star it 🌟. [Callstack][callstack-readme-with-love] is a group of React and React Native geeks, contact us at [hello@callstack.com](mailto:hello@callstack.com) if you need any help with these or just want to say hi!
+
 <!-- badges -->
 [build-badge]: https://img.shields.io/circleci/project/github/callstack/react-native-paper/master.svg?style=flat-square
 [build]: https://circleci.com/gh/callstack/react-native-paper
@@ -53,6 +58,9 @@ Read the [contribution guidelines](/CONTRIBUTING.md) before contributing.
 [prs-welcome]: http://makeapullrequest.com
 [chat-badge]: https://img.shields.io/discord/426714625279524876.svg?style=flat-square&colorB=758ED3
 [chat]: https://discord.gg/zwR2Cdh
+[callstack-badge]: https://callstack.com/images/callstack-badge.svg
+[callstack]: https://callstack.com/open-source/?utm_source=github.com&utm_medium=referral&utm_campaign=react-native-paper&utm_term=readme-badge
+[callstack-readme-with-love]: https://callstack.com/?utm_source=github.com&utm_medium=referral&utm_campaign=react-native-paper&utm_term=readme-with-love
 
 ## Contributors
 
diff --git a/docs/.babelrc b/docs/.babelrc
deleted file mode 100644
index 6486134f4..000000000
--- a/docs/.babelrc
+++ /dev/null
@@ -1,11 +0,0 @@
-{
-  "presets": [
-    "@babel/preset-env",
-    "@babel/preset-react",
-    "@babel/preset-flow",
-    "linaria/babel"
-  ],
-  "plugins": [
-    "@babel/plugin-proposal-class-properties"
-  ]
-}
diff --git a/docs/package.json b/docs/package.json
index 0b0114abc..2f0c3f225 100644
--- a/docs/package.json
+++ b/docs/package.json
@@ -23,7 +23,7 @@
   "license": "MIT",
   "dependencies": {
     "color": "^2.0.1",
-    "component-docs": "^0.18.1",
-    "linaria": "^1.1.4"
+    "component-docs": "^0.19.5",
+    "linaria": "^1.2.4"
   }
 }
diff --git a/docs/pages/4.react-native-web.md b/docs/pages/4.react-native-web.md
index 69e4e52e7..8d6ae2afc 100644
--- a/docs/pages/4.react-native-web.md
+++ b/docs/pages/4.react-native-web.md
@@ -16,6 +16,63 @@ To install `react-native-web`, run:
 yarn add react-native-web react-dom react-art
 ```
 
+### Using CRA ([Create React App](https://github.com/facebook/create-react-app))
+
+Install [`react-app-rewired`](https://github.com/timarney/react-app-rewired) to override `webpack` configuration:
+
+```sh
+yarn add --dev react-app-rewired
+```
+
+[Configure `babel-loader`](#2-configure-babel-loader) using a new file called `config-overrides.js`:
+
+```js
+module.exports = function override(config, env) {
+  config.module.rules.push({
+    test: /\.js$/,
+    exclude: /node_modules[/\\](?!react-native-paper|react-native-vector-icons|react-native-safe-area-view)/,
+    use: {
+      loader: "babel-loader",
+      options: {
+        // Disable reading babel configuration
+        babelrc: false,
+        configFile: false,
+
+        // The configration for compilation
+        presets: [
+          ["@babel/preset-env", { useBuiltIns: "usage" }],
+          "@babel/preset-react",
+          "@babel/preset-flow"
+        ],
+        plugins: [
+          "@babel/plugin-proposal-class-properties",
+          "@babel/plugin-proposal-object-rest-spread"
+        ]
+      }
+    }
+  });
+
+  return config;
+};
+```
+
+Change your script in `package.json`:
+
+```diff
+/* package.json */
+
+  "scripts": {
+-   "start": "react-scripts start",
++   "start": "react-app-rewired start",
+-   "build": "react-scripts build",
++   "build": "react-app-rewired build",
+-   "test": "react-scripts test --env=jsdom",
++   "test": "react-app-rewired test --env=jsdom"
+}
+```
+
+### Custom webpack setup
+
 To install `webpack`, run:
 
 ```sh
diff --git a/docs/yarn.lock b/docs/yarn.lock
index 725eebd23..e9488b297 100644
--- a/docs/yarn.lock
+++ b/docs/yarn.lock
@@ -405,6 +405,13 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-syntax-typescript@^7.2.0":
+  version "7.3.3"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.3.3.tgz#a7cc3f66119a9f7ebe2de5383cce193473d65991"
+  integrity sha512-dGwbSMA1YhVS8+31CnPR7LB4pcbrzcV99wQzby4uAfrkZPYZlQ7ImwdpzLqi6Z6IL02b8IAL379CaMwo0x5Lag==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+
 "@babel/plugin-transform-arrow-functions@^7.2.0":
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.2.0.tgz#9aeafbe4d6ffc6563bf8f8372091628f00779550"
@@ -658,6 +665,14 @@
   dependencies:
     "@babel/helper-plugin-utils" "^7.0.0"
 
+"@babel/plugin-transform-typescript@^7.3.2":
+  version "7.3.2"
+  resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.3.2.tgz#59a7227163e55738842f043d9e5bd7c040447d96"
+  integrity sha512-Pvco0x0ZSCnexJnshMfaibQ5hnK8aUHSvjCQhC1JR8eeg+iBwt0AtCO7gWxJ358zZevuf9wPSO5rv+WJcbHPXQ==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-syntax-typescript" "^7.2.0"
+
 "@babel/plugin-transform-unicode-regex@^7.2.0":
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.2.0.tgz#4eb8db16f972f8abb5062c161b8b115546ade08b"
@@ -790,6 +805,14 @@
     "@babel/plugin-transform-react-jsx-self" "^7.0.0"
     "@babel/plugin-transform-react-jsx-source" "^7.0.0"
 
+"@babel/preset-typescript@^7.3.3":
+  version "7.3.3"
+  resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.3.3.tgz#88669911053fa16b2b276ea2ede2ca603b3f307a"
+  integrity sha512-mzMVuIP4lqtn4du2ynEfdO0+RYcslwrZiJHXu4MGaC1ctJiW2fyaeDrtjJGs7R/KebZ1sgowcIoWf4uRpEfKEg==
+  dependencies:
+    "@babel/helper-plugin-utils" "^7.0.0"
+    "@babel/plugin-transform-typescript" "^7.3.2"
+
 "@babel/register@^7.0.0":
   version "7.0.0"
   resolved "https://registry.yarnpkg.com/@babel/register/-/register-7.0.0.tgz#fa634bae1bfa429f60615b754fc1f1d745edd827"
@@ -1834,16 +1857,17 @@ commondir@^1.0.1:
   resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b"
   integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=
 
-component-docs@^0.18.1:
-  version "0.18.1"
-  resolved "https://registry.yarnpkg.com/component-docs/-/component-docs-0.18.1.tgz#702168b459ed60fbb7858f5bd9cae5425904f382"
-  integrity sha512-FT3kO8ndHxbNXc0nz9ACEyZ2eYXhGG0IQRr/jFd/8Xlg5ZD2a1OoHwipdE2wec8PMpYYjh7MYrdaLb1ZFtuz8g==
+component-docs@^0.19.5:
+  version "0.19.5"
+  resolved "https://registry.yarnpkg.com/component-docs/-/component-docs-0.19.5.tgz#0ac3e8b0b1590d1504e6749bdcdec3dcd9f46543"
+  integrity sha512-175qW0CxJCwCiBy2+j0H8Xxf35nJkqIyAQaLDdCMSPa8g3TlfsUtfxMRB9ynQ5Ac9QY2tDid3CiElTXOeaCuuA==
   dependencies:
     "@babel/core" "^7.3.3"
     "@babel/plugin-proposal-class-properties" "^7.3.3"
     "@babel/preset-env" "^7.3.1"
     "@babel/preset-flow" "^7.0.0"
     "@babel/preset-react" "^7.0.0"
+    "@babel/preset-typescript" "^7.3.3"
     "@babel/register" "^7.0.0"
     "@mdx-js/mdx" "^0.17.5"
     "@mdx-js/tag" "^0.17.5"
@@ -1865,7 +1889,7 @@ component-docs@^0.18.1:
     hast-util-to-string "^1.0.1"
     history "^4.7.2"
     ignore-styles "^5.0.1"
-    linaria "^1.1.4"
+    linaria "^1.2.1"
     marked "^0.6.1"
     mini-css-extract-plugin "^0.5.0"
     opn "^5.4.0"
@@ -3792,10 +3816,36 @@ lcid@^2.0.0:
   dependencies:
     invert-kv "^2.0.0"
 
-linaria@^1.1.4:
-  version "1.1.4"
-  resolved "https://registry.yarnpkg.com/linaria/-/linaria-1.1.4.tgz#ef833e25626a897b89ad218081869860611aee31"
-  integrity sha512-XHK/doHf1FkDqXU/mQGxJLLfHatlM0RTr5Yx1+c97EOc/RRvwSvsMrtQ9VgefSNYllk+yJ5h3hkrPaf1PJeEww==
+linaria@^1.2.1:
+  version "1.2.1"
+  resolved "https://registry.yarnpkg.com/linaria/-/linaria-1.2.1.tgz#c830c295258b7b03ef0a7b3ee5b82b9414895846"
+  integrity sha512-2EpJhomogU2UQAJLsabL667lgIYz2+RemWboqn/WF6ULgN0+0ZarsjPiIm7BGTgglEo2F9Cs71lxYPKR1InGug==
+  dependencies:
+    "@babel/core" "^7.3.3"
+    "@babel/generator" "^7.3.3"
+    "@babel/plugin-proposal-export-namespace-from" "^7.2.0"
+    "@babel/plugin-syntax-dynamic-import" "^7.2.0"
+    "@babel/plugin-transform-modules-commonjs" "^7.2.0"
+    "@babel/register" "^7.0.0"
+    "@emotion/is-prop-valid" "^0.7.3"
+    cosmiconfig "^5.1.0"
+    dedent "^0.7.0"
+    enhanced-resolve "^4.1.0"
+    glob "^7.1.3"
+    loader-utils "^1.2.3"
+    mkdirp "^0.5.1"
+    normalize-path "^3.0.0"
+    postcss "^7.0.14"
+    react-is "^16.8.3"
+    rollup-pluginutils "^2.4.1"
+    source-map "^0.7.3"
+    stylis "^3.5.4"
+    yargs "^13.2.1"
+
+linaria@^1.2.4:
+  version "1.2.4"
+  resolved "https://registry.yarnpkg.com/linaria/-/linaria-1.2.4.tgz#5ef2a8b14b8a2071939b5772964fd402a02fa6c6"
+  integrity sha512-b95yQdpvE5X9JZ5/ChQpgECw9ERk3lBR/Th8fmNKzISgrkl1COgFX9l7zmjTNKp7jUWIGaVfN4hDuM0bPe/4JQ==
   dependencies:
     "@babel/core" "^7.3.3"
     "@babel/generator" "^7.3.3"
diff --git a/example/src/Examples/Dialogs/DialogWithLongText.js b/example/src/Examples/Dialogs/DialogWithLongText.js
index fe76eb684..c2d953b5b 100644
--- a/example/src/Examples/Dialogs/DialogWithLongText.js
+++ b/example/src/Examples/Dialogs/DialogWithLongText.js
@@ -1,7 +1,7 @@
 /* @flow */
 
 import * as React from 'react';
-import { ScrollView } from 'react-native';
+import { Dimensions, ScrollView } from 'react-native';
 import { Paragraph, Button, Portal, Dialog } from 'react-native-paper';
 
 const DialogWithLongText = ({
@@ -12,9 +12,13 @@ const DialogWithLongText = ({
   close: Function,
 }) => (
   <Portal>
-    <Dialog onDismiss={close} visible={visible}>
+    <Dialog
+      onDismiss={close}
+      visible={visible}
+      style={{ maxHeight: 0.6 * Dimensions.get('window').height }}
+    >
       <Dialog.Title>Alert</Dialog.Title>
-      <Dialog.ScrollArea style={{ maxHeight: 220, paddingHorizontal: 0 }}>
+      <Dialog.ScrollArea style={{ paddingHorizontal: 0 }}>
         <ScrollView contentContainerStyle={{ paddingHorizontal: 24 }}>
           <Paragraph>
             Material is the metaphor
@@ -36,6 +40,24 @@ const DialogWithLongText = ({
             conveying how objects move, interact, and exist in space and in
             relation to each other. Realistic lighting shows seams, divides
             space, and indicates moving parts.
+            {'\n'}
+            {'\n'}A material metaphor is the unifying theory of a rationalized
+            space and a system of motion. The material is grounded in tactile
+            reality, inspired by the study of paper and ink, yet technologically
+            advanced and open to imagination and magic.
+            {'\n'}
+            {'\n'}
+            Surfaces and edges of the material provide visual cues that are
+            grounded in reality. The use of familiar tactile attributes helps
+            users quickly understand affordances. Yet the flexibility of the
+            material creates new affordances that supersede those in the
+            physical world, without breaking the rules of physics.
+            {'\n'}
+            {'\n'}
+            The fundamentals of light, surface, and movement are key to
+            conveying how objects move, interact, and exist in space and in
+            relation to each other. Realistic lighting shows seams, divides
+            space, and indicates moving parts.
           </Paragraph>
         </ScrollView>
       </Dialog.ScrollArea>
diff --git a/example/src/Examples/ListSectionExample.js b/example/src/Examples/ListSectionExample.js
index 3bdb3229a..a44097ac6 100644
--- a/example/src/Examples/ListSectionExample.js
+++ b/example/src/Examples/ListSectionExample.js
@@ -19,7 +19,8 @@ class ListSectionExample extends React.Component<Props> {
     } = this.props;
     return (
       <ScrollView style={[styles.container, { backgroundColor: background }]}>
-        <List.Section title="Single line">
+        <List.Section>
+          <List.Subheader>Single line</List.Subheader>
           <List.Item
             left={props => <List.Icon {...props} icon="event" />}
             title="List item 1"
@@ -30,7 +31,8 @@ class ListSectionExample extends React.Component<Props> {
           />
         </List.Section>
         <Divider />
-        <List.Section title="Two line">
+        <List.Section>
+          <List.Subheader>Two line</List.Subheader>
           <List.Item
             left={() => (
               <Image
@@ -54,7 +56,8 @@ class ListSectionExample extends React.Component<Props> {
           />
         </List.Section>
         <Divider />
-        <List.Section title="Three line">
+        <List.Section>
+          <List.Subheader>Three line</List.Subheader>
           <List.Item
             left={() => (
               <Image
diff --git a/package.json b/package.json
index bdbabb9d5..0501e0de3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "react-native-paper",
-  "version": "2.11.1",
+  "version": "2.13.0",
   "description": "Material design for React Native",
   "main": "src/index.js",
   "typings": "typings/index.d.ts",
@@ -100,7 +100,7 @@
   "husky": {
     "hooks": {
       "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
-      "pre-commit": "yarn lint && yarn flow && yarn typescript && yarn test"
+      "pre-commit": "yarn lint && yarn flow check && yarn typescript && yarn test"
     }
   },
   "jest": {
@@ -111,5 +111,15 @@
     "watchPathIgnorePatterns": [
       "__fixtures__\\/[^/]+\\/(output|error)\\.js"
     ]
+  },
+  "greenkeeper": {
+    "ignore": [
+      "expo",
+      "flow-bin",
+      "react",
+      "react-dom",
+      "react-native",
+      "react-test-renderer"
+    ]
   }
 }
diff --git a/src/components/Button.js b/src/components/Button.js
index 9fde2b7af..6625fab5a 100644
--- a/src/components/Button.js
+++ b/src/components/Button.js
@@ -61,7 +61,8 @@ type Props = React.ElementConfig<typeof Surface> & {|
    */
   onPress?: () => mixed,
   /**
-   * Style of button's inner content
+   * Style of button's inner content.
+   * Use this prop to apply custom height and width.
    */
   contentStyle?: any,
   style?: any,
diff --git a/src/components/Dialog/Dialog.js b/src/components/Dialog/Dialog.js
index d52a0eb9d..a58b95551 100644
--- a/src/components/Dialog/Dialog.js
+++ b/src/components/Dialog/Dialog.js
@@ -18,7 +18,7 @@ type Props = {|
   /**
    * Callback that is called when the user dismisses the dialog.
    */
-  onDismiss: () => mixed,
+  onDismiss?: () => mixed,
   /**
    * Determines Whether the dialog is visible.
    */
diff --git a/src/components/Dialog/DialogScrollArea.js b/src/components/Dialog/DialogScrollArea.js
index 533a124ba..e67466db6 100644
--- a/src/components/Dialog/DialogScrollArea.js
+++ b/src/components/Dialog/DialogScrollArea.js
@@ -64,6 +64,8 @@ const styles = StyleSheet.create({
     borderTopWidth: StyleSheet.hairlineWidth,
     borderBottomWidth: StyleSheet.hairlineWidth,
     paddingHorizontal: 24,
+    flexGrow: 1,
+    flexShrink: 1,
   },
 });
 
diff --git a/src/components/List/List.js b/src/components/List/List.js
index 921e556de..91c14fbf9 100644
--- a/src/components/List/List.js
+++ b/src/components/List/List.js
@@ -11,3 +11,6 @@ export { default as Item } from './ListItem';
 
 // @component ./ListSection.js
 export { default as Section } from './ListSection';
+
+// @component ./ListSubheader.js
+export { default as Subheader } from './ListSubheader';
diff --git a/src/components/List/ListSection.js b/src/components/List/ListSection.js
index dd7da2480..1091c63e0 100644
--- a/src/components/List/ListSection.js
+++ b/src/components/List/ListSection.js
@@ -1,9 +1,8 @@
 /* @flow */
 
-import color from 'color';
 import * as React from 'react';
 import { View, StyleSheet } from 'react-native';
-import Text from '../Typography/Text';
+import ListSubheader from './ListSubheader';
 import { withTheme } from '../../core/theming';
 import type { Theme } from '../../types';
 
@@ -38,7 +37,8 @@ type Props = React.ElementConfig<typeof View> & {
  * export default class MyComponent extends React.Component {
  *   render() {
  *     return (
- *       <List.Section title="Some title">
+ *       <List.Section>
+ *         <List.Subheader>Some title</List.Subheader>
  *         <List.Item
  *           title="First Item"
  *           left={() => <List.Icon icon="folder" />}
@@ -59,25 +59,11 @@ class ListSection extends React.Component<Props> {
   static displayName = 'List.Section';
 
   render() {
-    const { children, title, theme, style, ...rest } = this.props;
-    const { colors, fonts } = theme;
-
-    const titleColor = color(colors.text)
-      .alpha(0.54)
-      .rgb()
-      .string();
-    const fontFamily = fonts.medium;
+    const { children, title, style, ...rest } = this.props;
 
     return (
       <View {...rest} style={[styles.container, style]}>
-        {title && (
-          <Text
-            numberOfLines={1}
-            style={[styles.title, { color: titleColor, fontFamily }]}
-          >
-            {title}
-          </Text>
-        )}
+        {title && <ListSubheader>{title}</ListSubheader>}
         {children}
       </View>
     );
@@ -88,10 +74,6 @@ const styles = StyleSheet.create({
   container: {
     marginVertical: 8,
   },
-  title: {
-    marginVertical: 13,
-    marginHorizontal: 16,
-  },
 });
 
 export default withTheme(ListSection);
diff --git a/src/components/List/ListSubheader.js b/src/components/List/ListSubheader.js
new file mode 100644
index 000000000..2eb9b21c0
--- /dev/null
+++ b/src/components/List/ListSubheader.js
@@ -0,0 +1,64 @@
+/* @flow */
+
+import * as React from 'react';
+import { StyleSheet } from 'react-native';
+import color from 'color';
+import Text from '../Typography/Text';
+import { withTheme } from '../../core/theming';
+import type { Theme } from '../../types';
+
+type Props = React.ElementConfig<typeof Text> & {
+  /**
+   * @optional
+   */
+  theme: Theme,
+  style?: any,
+};
+
+/**
+ * A component used to display a header in lists.
+ *
+ * ## Usage
+ * ```js
+ * import * as React from 'react';
+ * import { List } from 'react-native-paper';
+ *
+ * class MyComponent extends React.Component {
+ *  render () {
+ *    return <List.Subheader>My List Title</List.Subheader>;
+ *  }
+ * }
+ *
+ * export default MyComponent;
+ * ```
+ */
+class ListSubheader extends React.Component<Props> {
+  static displayName = 'List.Subheader';
+
+  render() {
+    const { style, theme, ...rest } = this.props;
+    const { colors, fonts } = theme;
+    const fontFamily = fonts.medium;
+    const textColor = color(colors.text)
+      .alpha(0.54)
+      .rgb()
+      .string();
+
+    return (
+      <Text
+        numberOfLines={1}
+        {...rest}
+        style={[styles.container, { color: textColor, fontFamily }, style]}
+      />
+    );
+  }
+}
+
+const styles = StyleSheet.create({
+  container: {
+    paddingHorizontal: 16,
+    paddingVertical: 13,
+  },
+});
+
+export default withTheme(ListSubheader);
diff --git a/src/components/Modal.js b/src/components/Modal.js
index c66412b77..cc58f7382 100644
--- a/src/components/Modal.js
+++ b/src/components/Modal.js
@@ -22,7 +22,7 @@ type Props = {|
   /**
    * Callback that is called when the user dismisses the modal.
    */
-  onDismiss: () => mixed,
+  onDismiss?: () => mixed,
   /**
    * Determines Whether the modal is visible.
    */
@@ -116,6 +116,7 @@ class Modal extends React.Component<Props, State> {
   };
 
   _showModal = () => {
+    BackHandler.removeEventListener('hardwareBackPress', this._handleBack);
     BackHandler.addEventListener('hardwareBackPress', this._handleBack);
     Animated.timing(this.state.opacity, {
       toValue: 1,
@@ -147,6 +148,10 @@ class Modal extends React.Component<Props, State> {
     });
   };
 
+  componentWillUnmount() {
+    BackHandler.removeEventListener('hardwareBackPress', this._handleBack);
+  }
+
   render() {
     if (!this.state.rendered) return null;
 
diff --git a/src/components/Searchbar.js b/src/components/Searchbar.js
index b3c64a9cb..f97c97978 100644
--- a/src/components/Searchbar.js
+++ b/src/components/Searchbar.js
@@ -30,7 +30,12 @@ type Props = React.ElementConfig<typeof TextInput> & {|
    * Callback to execute if we want the left icon to act as button.
    */
   onIconPress?: () => mixed,
+  /**
+   * Set style of the TextInput component inside the searchbar
+   */
+  inputStyle?: any,
   style?: any,
+
   /**
    * @optional
    */
@@ -118,6 +123,7 @@ class Searchbar extends React.Component<Props> {
       value,
       theme,
       style,
+      inputStyle,
       ...rest
     } = this.props;
     const { colors, roundness, dark, fonts } = theme;
@@ -150,7 +156,7 @@ class Searchbar extends React.Component<Props> {
           icon={icon || 'search'}
         />
         <TextInput
-          style={[styles.input, { color: textColor, fontFamily }]}
+          style={[styles.input, { color: textColor, fontFamily }, inputStyle]}
           placeholder={placeholder || ''}
           placeholderTextColor={colors.placeholder}
           selectionColor={colors.primary}
diff --git a/src/components/Snackbar.js b/src/components/Snackbar.js
index 1520c7db9..ba7c66a2f 100644
--- a/src/components/Snackbar.js
+++ b/src/components/Snackbar.js
@@ -165,7 +165,13 @@ class Snackbar extends React.Component<Props, State> {
     }).start(({ finished }) => {
       if (finished) {
         const { duration } = this.props;
-        this._hideTimeout = setTimeout(this.props.onDismiss, duration);
+        const isInfinity =
+          duration === Number.POSITIVE_INFINITY ||
+          duration === Number.NEGATIVE_INFINITY;
+
+        if (finished && !isInfinity) {
+          this._hideTimeout = setTimeout(this.props.onDismiss, duration);
+        }
       }
     });
   };
diff --git a/typings/components/Card.d.ts b/typings/components/Card.d.ts
index 3a7a4ba0d..6ec68ee27 100644
--- a/typings/components/Card.d.ts
+++ b/typings/components/Card.d.ts
@@ -28,6 +28,7 @@ export interface CardTitleProps extends ViewProps {
 export interface CardProps {
   elevation?: number;
   onPress?: () => any;
+  onLongPress?: () => any;
   children: React.ReactNode;
   style?: any;
   theme?: ThemeShape;
diff --git a/typings/components/Dialog.d.ts b/typings/components/Dialog.d.ts
index f8fdda0f6..1d4c9742d 100644
--- a/typings/components/Dialog.d.ts
+++ b/typings/components/Dialog.d.ts
@@ -15,7 +15,7 @@ export interface DialogTitleProps extends DialogBaseProps {
 
 export interface DialogProps {
   dismissable?: boolean;
-  onDismiss: () => any;
+  onDismiss?: () => any;
   visible: boolean;
   children: React.ReactNode;
   style?: any;
diff --git a/typings/components/List.d.ts b/typings/components/List.d.ts
index 310670c2d..993094e00 100644
--- a/typings/components/List.d.ts
+++ b/typings/components/List.d.ts
@@ -1,5 +1,5 @@
 import * as React from 'react';
-import { StyleProp, ViewStyle, ViewProps } from 'react-native';
+import { StyleProp, ViewStyle, ViewProps, TextProps, TextStyle } from 'react-native';
 import { ThemeShape, IconSource } from '../types';
 import { TouchableRipplePropsWithoutChildren } from './TouchableRipple';
 
@@ -40,3 +40,10 @@ export interface SectionProps extends ViewProps {
 }
 
 export declare class Section extends React.Component<SectionProps> {}
+
+export interface SubheaderProps extends TextProps {
+  style?: StyleProp<TextStyle>;
+  theme?: ThemeShape
+}
+
+export declare class Subheader extends React.Component<SubheaderProps> {}
diff --git a/typings/components/Modal.d.ts b/typings/components/Modal.d.ts
index 5cb1c2dfb..b1f1564ff 100644
--- a/typings/components/Modal.d.ts
+++ b/typings/components/Modal.d.ts
@@ -3,7 +3,7 @@ import { ThemeShape } from '../types';
 
 export interface ModalProps {
   dismissable?: boolean;
-  onDismiss: () => any;
+  onDismiss?: () => any;
   visible: boolean;
   children: React.ReactNode;
   theme?: ThemeShape;


Test patch:
diff --git a/src/components/__tests__/ListSection.test.js b/src/components/__tests__/ListSection.test.js
index 0a760f88e..190e48e90 100644
--- a/src/components/__tests__/ListSection.test.js
+++ b/src/components/__tests__/ListSection.test.js
@@ -5,8 +5,9 @@ import renderer from 'react-test-renderer';
 import ListSection from '../List/ListSection';
 import ListItem from '../List/ListItem';
 import ListIcon from '../List/ListIcon';
+import ListSubheader from '../List/ListSubheader';
 
-it('renders list section without title', () => {
+it('renders list section without subheader', () => {
   const tree = renderer
     .create(
       <ListSection>
@@ -25,10 +26,11 @@ it('renders list section without title', () => {
   expect(tree).toMatchSnapshot();
 });
 
-it('renders list section with title', () => {
+it('renders list section with subheader', () => {
   const tree = renderer
     .create(
-      <ListSection title="Some title">
+      <ListSection>
+        <ListSubheader>Some title</ListSubheader>
         <ListItem
           title="First Item"
           left={props => <ListIcon {...props} icon="folder" />}
diff --git a/src/components/__tests__/__snapshots__/ListSection.test.js.snap b/src/components/__tests__/__snapshots__/ListSection.test.js.snap
index ccb243031..9bf48f8c2 100644
--- a/src/components/__tests__/__snapshots__/ListSection.test.js.snap
+++ b/src/components/__tests__/__snapshots__/ListSection.test.js.snap
@@ -1,6 +1,6 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`renders list section with title 1`] = `
+exports[`renders list section with subheader 1`] = `
 <View
   style={
     Array [
@@ -10,6 +10,30 @@ exports[`renders list section with title 1`] = `
       undefined,
     ]
   }
+  theme={
+    Object {
+      "colors": Object {
+        "accent": "#03dac4",
+        "backdrop": "rgba(0, 0, 0, 0.5)",
+        "background": "#f6f6f6",
+        "disabled": "rgba(0, 0, 0, 0.26)",
+        "error": "#B00020",
+        "notification": "#f50057",
+        "placeholder": "rgba(0, 0, 0, 0.54)",
+        "primary": "#6200ee",
+        "surface": "#ffffff",
+        "text": "#000000",
+      },
+      "dark": false,
+      "fonts": Object {
+        "light": "HelveticaNeue-Light",
+        "medium": "HelveticaNeue-Medium",
+        "regular": "Helvetica Neue",
+        "thin": "HelveticaNeue-Thin",
+      },
+      "roundness": 4,
+    }
+  }
 >
   <Text
     numberOfLines={1}
@@ -23,13 +47,14 @@ exports[`renders list section with title 1`] = `
         },
         Array [
           Object {
-            "marginHorizontal": 16,
-            "marginVertical": 13,
+            "paddingHorizontal": 16,
+            "paddingVertical": 13,
           },
           Object {
             "color": "rgba(0, 0, 0, 0.54)",
             "fontFamily": "HelveticaNeue-Medium",
           },
+          undefined,
         ],
       ]
     }
@@ -275,7 +300,7 @@ exports[`renders list section with title 1`] = `
 </View>
 `;
 
-exports[`renders list section without title 1`] = `
+exports[`renders list section without subheader 1`] = `
 <View
   style={
     Array [
@@ -285,6 +310,30 @@ exports[`renders list section without title 1`] = `
       undefined,
     ]
   }
+  theme={
+    Object {
+      "colors": Object {
+        "accent": "#03dac4",
+        "backdrop": "rgba(0, 0, 0, 0.5)",
+        "background": "#f6f6f6",
+        "disabled": "rgba(0, 0, 0, 0.26)",
+        "error": "#B00020",
+        "notification": "#f50057",
+        "placeholder": "rgba(0, 0, 0, 0.54)",
+        "primary": "#6200ee",
+        "surface": "#ffffff",
+        "text": "#000000",
+      },
+      "dark": false,
+      "fonts": Object {
+        "light": "HelveticaNeue-Light",
+        "medium": "HelveticaNeue-Medium",
+        "regular": "Helvetica Neue",
+        "thin": "HelveticaNeue-Thin",
+      },
+      "roundness": 4,
+    }
+  }
 >
   <View
     accessible={true}
diff --git a/src/components/__tests__/__snapshots__/Searchbar.test.js.snap b/src/components/__tests__/__snapshots__/Searchbar.test.js.snap
index fd016c3a2..6fd256197 100644
--- a/src/components/__tests__/__snapshots__/Searchbar.test.js.snap
+++ b/src/components/__tests__/__snapshots__/Searchbar.test.js.snap
@@ -120,6 +120,7 @@ exports[`renders with placeholder 1`] = `
           "color": "#000000",
           "fontFamily": "Helvetica Neue",
         },
+        undefined,
       ]
     }
     underlineColorAndroid="transparent"
@@ -332,6 +333,7 @@ exports[`renders with text 1`] = `
           "color": "#000000",
           "fontFamily": "Helvetica Neue",
         },
+        undefined,
       ]
     }
     underlineColorAndroid="transparent"