Repository: speee/jsx-slack (ts)

Issue:
<Call> layout block component
[Calls API](https://api.slack.com/apis/calls) provides a way for call app to tell Slack the calling. In the context of Block Kit, [the `call` type for block element](https://api.slack.com/apis/calls#3._post_the_call_to_channel) has been available.

We will consider to add `<Call>` layout block to support indicating the call by your app.

```jsx
<Blocks>
  <Section>
    <a href="@U1234567890" />, you've got a call!
  </Section>
  <Call callId="R123" />
</Blocks>
```

The above JSX will transform into following:

```json
[
  {
    "type": "section",
    "text": {
      "type": "mrkdwn",
      "text": "<@U1234567890>, you've got a call!",
      "verbatim": true
    }
  },
  {
    "type": "call",
    "call_id": "R123"
  }
]
```

[![](https://user-images.githubusercontent.com/3993388/82516507-e7b2c680-9b55-11ea-8561-117819b2fc3c.jpeg)](https://api.slack.com/tools/block-kit-builder?mode=message&blocks=%5B%7B%22type%22%3A%22section%22%2C%22text%22%3A%7B%22type%22%3A%22mrkdwn%22%2C%22text%22%3A%22%3C%40U1234567890%3E%2C%20you%27ve%20got%20a%20call!%22%2C%22verbatim%22%3Atrue%7D%7D%2C%7B%22type%22%3A%22call%22%2C%22call_id%22%3A%22R123%22%7D%5D)

> **NOTE**: `<Call>` is available only in `<Blocks>` container as same as `<File>` layout block.

Pull request description:
Add <Call> component for the call layout block
`<Call>` layout block component shows a card of registered call into Slack workspace. The developer of the phone app can use a Slack-native card together with rich sections in around, powered by jsx-slack!

```jsx
<Blocks>
  <Section>
    <p>
      Hey <a href="@U0123456789" />! It's time to the daily standup with a remote team.
    </p>
    <ul>
      <li><b>Start / join to call:</b> Push <i>"Join"</i> button and follow the opened app.</li>
      <li>Are you busy to call now? <i>Please reply to the thread with today's plan.</i></li>
    </ul>
  </Section>
  <Call callId="R0123456789" />
  <Context>
    <i>
      Have you a trouble when opening our calling app? <a href="https://example.com">Please see our documentation.</a>
    </i>
  </Context>
</Blocks>
```

![](https://user-images.githubusercontent.com/3993388/82547280-6fb7c100-9b94-11ea-8b66-6d20ecc1e12e.png)

The detail of `call` layout block is not yet described in [the Block Kit reference](https://api.slack.com/reference/block-kit/blocks), but I've confirmed the `call` layout block is working with exist call ID.

Resolves #164.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c26eb25..08390d5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## [Unreleased]
 
+### Added
+
+- [`<Call>` layout block component](https://github.com/speee/jsx-slack/blob/master/docs/layout-blocks.md#call) to show a card of registered call ([#164](https://github.com/speee/jsx-slack/issues/164), [#165](https://github.com/speee/jsx-slack/pull/165))
+
 ## v2.1.0 - 2020-05-01
 
 ### Added
diff --git a/demo/schema.js b/demo/schema.js
index 07bddd1..540d1fc 100644
--- a/demo/schema.js
+++ b/demo/schema.js
@@ -75,7 +75,7 @@ const schema = {
   // Container components
   Blocks: {
     attrs: {},
-    children: [...commonKnownBlocks, 'File'],
+    children: [...commonKnownBlocks, 'File', 'Call'],
   },
 
   Modal: {
@@ -159,6 +159,10 @@ const schema = {
     attrs: { externalId: null, source: ['remote'], ...blockCommonAttrs },
     children: [],
   },
+  Call: {
+    attrs: { callId: null, ...blockCommonAttrs },
+    children: [],
+  },
 
   Input: {
     attrs: {
diff --git a/docs/jsx-components-for-block-kit.md b/docs/jsx-components-for-block-kit.md
index abedfcd..90476f6 100644
--- a/docs/jsx-components-for-block-kit.md
+++ b/docs/jsx-components-for-block-kit.md
@@ -17,6 +17,7 @@
 - [**`<Actions>`**: Actions Block](layout-blocks.md#actions)
 - [**`<Context>`**: Context Block](layout-blocks.md#context)
 - [**`<File>`**: File Block](layout-blocks.md#file) (Only for messaging)
+- [**`<Call>`**: Call Block](layout-blocks.md#call) (Only for messaging)
 - [**`<Input>`**: Input Block](layout-blocks.md#input) (Only for modal)
 
 ## **[Block elements](block-elements.md)**
diff --git a/docs/layout-blocks.md b/docs/layout-blocks.md
index 9540291..87a29e2 100644
--- a/docs/layout-blocks.md
+++ b/docs/layout-blocks.md
@@ -188,6 +188,21 @@ Display a remote file that was added to Slack workspace. [Learn about adding rem
 - `id` / `blockId` (optional): A string of unique identifier of block.
 - `source` (optional): Override `source` field. At the moment, you should not take care this because only the default value `remote` is available.
 
+## <a name="call" id="call"></a> `<Call>`: Call Block (Only for messaging)
+
+Display a card of the call that was registered to Slack workspace. [Learn about using the Calls API in the document of Slack API.](https://api.slack.com/apis/calls) _This block is only for [`<Blocks>` container](block-containers.md#blocks)._
+
+```jsx
+<Blocks>
+  <Call callId="R0123456789" />
+</Blocks>
+```
+
+### Props
+
+- `callId` (**required**): A string of registered call's ID.
+- `id` / `blockId` (optional): A string of unique identifier of block.
+
 ## <a name="input" id="input"></a> [`<Input>`: Input Block](https://api.slack.com/reference/messaging/blocks#input) (Only for modal)
 
 Display one of interactive components for input to collect information from users. _This block is only for [`<Modal>` container](block-containers.md#modal)._
diff --git a/src/block-kit/container/Blocks.ts b/src/block-kit/container/Blocks.ts
index c8806be..f2225d0 100644
--- a/src/block-kit/container/Blocks.ts
+++ b/src/block-kit/container/Blocks.ts
@@ -24,6 +24,7 @@ const blockTypeFilter = (t) => t !== 'radio_buttons' && t !== 'checkboxes'
  * - `<Context>`
  * - `<Actions>`
  * - `<File>`
+ * - `<Call>`
  *
  * @example
  * ```jsx
@@ -49,6 +50,7 @@ export const Blocks = generateBlocksContainer({
     actions: generateActionsValidator(
       availableActionTypes.filter(blockTypeFilter)
     ),
+    call: true,
     context: true,
     divider: true,
     file: true,
diff --git a/src/block-kit/index.ts b/src/block-kit/index.ts
index 3a9c508..debab0a 100644
--- a/src/block-kit/index.ts
+++ b/src/block-kit/index.ts
@@ -5,6 +5,7 @@ export { Modal } from './container/Modal'
 
 // Layout blocks
 export { Actions } from './layout/Actions'
+export { Call } from './layout/Call'
 export { Context } from './layout/Context'
 export { Divider } from './layout/Divider'
 export { File } from './layout/File'
diff --git a/src/block-kit/layout/Call.ts b/src/block-kit/layout/Call.ts
new file mode 100644
index 0000000..b8e5be8
--- /dev/null
+++ b/src/block-kit/layout/Call.ts
@@ -0,0 +1,31 @@
+import { Block } from '@slack/types'
+import { LayoutBlockProps } from './utils'
+import { createComponent } from '../../jsx'
+
+type CallBlock = Block & {
+  type: 'call'
+  call_id: string
+}
+
+export interface CallProps extends LayoutBlockProps {
+  children?: never
+
+  /** A string of registered call's ID. */
+  callId: string
+}
+
+/**
+ * The `call` layout block to display your registered call to Slack.
+ *
+ * _This component is available only in `<Blocks>` container for messaging._
+ *
+ * Learn about {@link https://api.slack.com/apis/calls|using the Calls API} in
+ * the document of Slack API.
+ *
+ * @return The partial JSON for `call` layout block
+ */
+export const Call = createComponent<CallProps, CallBlock>('Call', (props) => ({
+  type: 'call',
+  block_id: props.blockId || props.id,
+  call_id: props.callId,
+}))


Test patch:
diff --git a/test/block-kit/container-components.tsx b/test/block-kit/container-components.tsx
index 8153fed..b101811 100644
--- a/test/block-kit/container-components.tsx
+++ b/test/block-kit/container-components.tsx
@@ -2,6 +2,7 @@
 import { PlainTextElement, View } from '@slack/types'
 import JSXSlack, {
   Blocks,
+  Call,
   Escape,
   File,
   Home,
@@ -116,7 +117,6 @@ describe('Container components', () => {
     })
 
     it('throws error when <Modal> has unexpected element', () => {
-      // <Modal> cannot use file block
       expect(() =>
         JSXSlack(
           <Modal title="test">
@@ -124,6 +124,14 @@ describe('Container components', () => {
           </Modal>
         )
       ).toThrow()
+
+      expect(() =>
+        JSXSlack(
+          <Modal title="test">
+            <Call callId="R01234567" />
+          </Modal>
+        )
+      ).toThrow()
     })
 
     it('ignores invalid literal values to keep compatibillity with v1', () => {
@@ -284,6 +292,14 @@ describe('Container components', () => {
         )
       ).toThrow()
 
+      expect(() =>
+        JSXSlack(
+          <Home>
+            <Call callId="R01234567" />
+          </Home>
+        )
+      ).toThrow()
+
       expect(() =>
         JSXSlack(
           <Home>
diff --git a/test/block-kit/layout-blocks.tsx b/test/block-kit/layout-blocks.tsx
index 7e2e03e..1097853 100644
--- a/test/block-kit/layout-blocks.tsx
+++ b/test/block-kit/layout-blocks.tsx
@@ -27,6 +27,7 @@ import JSXSlack, {
   Section,
   Select,
   UsersSelect,
+  Call,
 } from '../../src/index'
 
 beforeEach(() => JSXSlack.exactMode(false))
@@ -480,6 +481,26 @@ describe('Layout blocks', () => {
       ).toStrictEqual([{ ...file, source: 'local' }]))
   })
 
+  describe('<Call>', () => {
+    it('outputs call block', () => {
+      expect(
+        <Blocks>
+          <Call id="call_block" callId="R01234567" />
+        </Blocks>
+      ).toStrictEqual([
+        {
+          type: 'call',
+          block_id: 'call_block',
+          call_id: 'R01234567',
+        },
+      ])
+
+      expect(<Call id="abc" callId="R123" />).toStrictEqual(
+        <Call blockId="abc" callId="R123" />
+      )
+    })
+  })
+
   describe('<Input> (layout block)', () => {
     it('outputs input block with wrapped element', () => {
       const select = (