Repository: speee/jsx-slack
Pull request #62 (2019-09-30 08:56:36)

Add <Textarea> component
This PR has implemented a part of #57.

`<Textarea>` component is just an alias to `<Input>` block element except difference of `multiline` field in plain-text input element.

```jsx
<Modal title="My App">
  <Textarea
    label="Tweet"
    name="tweet"
    placeholder="What’s happening?"
    maxLength={280}
    required
  />
</Modal>
```

[<img width="585" alt="textarea" src="https://user-images.githubusercontent.com/3993388/65863683-962daf80-e3ab-11e9-8f64-b19d0e040f1a.png">](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Tweet%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22tweet%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22What%E2%80%99s%20happening%3F%22%2C%22emoji%22%3Afalse%7D%2C%22multiline%22%3Atrue%2C%22max_length%22%3A280%7D%7D%5D&mode=modal)


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5e45441..d13c96d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,7 @@
 - [Multi-select menus](https://api.slack.com/reference/block-kit/block-elements#multi_select) ([#56](https://github.com/speee/jsx-slack/issues/56), [#58](https://github.com/speee/jsx-slack/pull/58))
 - `<Modal>` container component ([#60](https://github.com/speee/jsx-slack/pull/60))
 - `<Input>` component for layout block and block element ([#61](https://github.com/speee/jsx-slack/pull/61))
+- `<Textarea>` component ([#62](https://github.com/speee/jsx-slack/pull/62))
 
 ### Changed
 
diff --git a/docs/jsx-components-for-block-kit.md b/docs/jsx-components-for-block-kit.md
index 2d75146..86149d2 100644
--- a/docs/jsx-components-for-block-kit.md
+++ b/docs/jsx-components-for-block-kit.md
@@ -563,7 +563,7 @@ It can place as children of `<Modal>` directly.
 
 > Internally this is syntactic sugar for [`<Input>` block](#input-block) with a plain-text input.
 
-#### Props (Block element)
+#### <a name="input-element-props" id="input-element-props">Props (Block element)</a>
 
 - `label` (**required**): The label string for the element.
 - `id` / `blockId` (optional): A string of unique identifier for [`<Input>` block](#input-block).
@@ -575,6 +575,28 @@ It can place as children of `<Modal>` directly.
 - `maxLength` (optional): The maximum number of characters allowed for the input element. It must up to 3000 character.
 - `minLength` (optional): The minimum number of characters allowed for the input element.
 
+### `<Textarea>`: Plain-text input element with multiline (Only for modal)
+
+`<Textarea>` component has very similar interface to [`<Input>` block element](#input-element). An only difference is to allow multi-line input.
+
+```jsx
+<Modal title="My App">
+  <Textarea
+    label="Tweet"
+    name="tweet"
+    placeholder="What’s happening?"
+    maxLength={280}
+    required
+  />
+</Modal>
+```
+
+[<img src="https://raw.githubusercontent.com/speee/jsx-slack/master/docs/preview-btn.svg?sanitize=true" width="240" />](https://api.slack.com/tools/block-kit-builder?blocks=%5B%7B%22type%22%3A%22input%22%2C%22label%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22Tweet%22%2C%22emoji%22%3Atrue%7D%2C%22optional%22%3Afalse%2C%22element%22%3A%7B%22type%22%3A%22plain_text_input%22%2C%22action_id%22%3A%22tweet%22%2C%22placeholder%22%3A%7B%22type%22%3A%22plain_text%22%2C%22text%22%3A%22What%E2%80%99s%20happening%3F%22%2C%22emoji%22%3Afalse%7D%2C%22multiline%22%3Atrue%2C%22max_length%22%3A280%7D%7D%5D&mode=modal)
+
+#### Props
+
+It is exactly same as [`<Input>` block element](#input-element-props).
+
 ## Components for [composition objects](https://api.slack.com/reference/messaging/composition-objects)
 
 ### [`<Confirm>`: Confirmation dialog](https://api.slack.com/reference/messaging/composition-objects#confirm)
diff --git a/src/block-kit/Input.tsx b/src/block-kit/Input.tsx
index 119f688..dc045ef 100644
--- a/src/block-kit/Input.tsx
+++ b/src/block-kit/Input.tsx
@@ -38,8 +38,11 @@ interface InputComponentProps extends InputCommonProps {
 }
 
 type InputProps = InputBlockProps | InputComponentProps
+type TextareaProps = InputComponentProps
 
-const InputComponent: JSXSlack.FC<InputComponentProps> = props => (
+const InputComponent: JSXSlack.FC<
+  InputComponentProps & { multiline?: boolean }
+> = props => (
   <Input
     blockId={props.blockId}
     hint={props.hint || props.title}
@@ -52,6 +55,7 @@ const InputComponent: JSXSlack.FC<InputComponentProps> = props => (
       initialValue={props.value}
       maxLength={coerceToInteger(props.maxLength)}
       minLength={coerceToInteger(props.minLength)}
+      multiline={props.multiline}
       placeholder={props.placeholder}
     />
   </Input>
@@ -71,3 +75,6 @@ export const Input: JSXSlack.FC<InputProps> = props => {
     />
   )
 }
+
+export const Textarea: JSXSlack.FC<TextareaProps> = props =>
+  InputComponent({ ...props, multiline: true })
diff --git a/src/block-kit/index.ts b/src/block-kit/index.ts
index 9b15faf..f70a3ef 100644
--- a/src/block-kit/index.ts
+++ b/src/block-kit/index.ts
@@ -8,7 +8,7 @@ export { Context } from './Context'
 export { Divider } from './Divider'
 export { File } from './File'
 export { Image } from './Image'
-export { Input } from './Input'
+export { Input, Textarea } from './Input'
 export { Section, Field } from './Section'
 
 // Block elements


Test patch:
diff --git a/test/index.tsx b/test/index.tsx
index 86b3616..1272444 100644
--- a/test/index.tsx
+++ b/test/index.tsx
@@ -35,6 +35,7 @@ import JSXSlack, {
   Section,
   Select,
   SelectFragment,
+  Textarea,
   UsersSelect,
 } from '../src/index'
 
@@ -1089,6 +1090,29 @@ describe('jsx-slack', () => {
         })
       })
     })
+
+    describe('<Textarea>', () => {
+      it('outputs input block with plain-text input element that is enabled multiline prop', () => {
+        const { blocks } = JSXSlack(
+          <Modal title="test">
+            <Textarea label="textarea" name="foobar" />
+          </Modal>
+        )
+
+        const expected: InputBlock = {
+          type: 'input',
+          label: { type: 'plain_text', text: 'textarea', emoji: true },
+          optional: true,
+          element: {
+            type: 'plain_text_input',
+            action_id: 'foobar',
+            multiline: true,
+          },
+        }
+
+        expect(blocks).toStrictEqual([expected])
+      })
+    })
   })
 
   describe('<Escape> component', () => {