Repository: speee/jsx-slack (ts)

Issue:
Allow using <RadioButtonGroup> and <CheckboxGroup> in message container <Blocks>
Slack has announced that checkboxes and radio buttons are available now in messages.
https://twitter.com/SlackAPI/status/1313510108152901632

![](https://user-images.githubusercontent.com/3993388/95230920-7726f080-083d-11eb-99f6-98842397c5ac.png)

jsx-slack does not have to prevent using `<CheckboxGroup>` and `<RadioButtonGroup>` in `<Blocks>` included `<Section>` and `<Actions>` anymore! Now we can remove block type filter for `<Blocks>` validation.

https://github.com/speee/jsx-slack/blob/09f39a198cd9c2e768f3851935c30f454f357c7b/src/block-kit/container/Blocks.ts#L12-L13

Pull request description:
Allow using <RadioButtonGroup> / <CheckboxGroup> in <Blocks>
Removed validation to restrict using `<RadioButtonGroup>` / `<CheckBoxGroup>` in `<Blocks>` container. Now all of interactive components can use within every available layout blocks.

Close #196.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c517310..da60846 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
 
 ### Changed
 
+- Allow using `<RadioButtonGroup>` and `<CheckboxGroup>` in message container `<Blocks>` ([#196](https://github.com/speee/jsx-slack/issues/196), [#197](https://github.com/speee/jsx-slack/pull/197))
 - Upgrade dependent packages to the latest version ([#191](https://github.com/speee/jsx-slack/pull/191), [#198](https://github.com/speee/jsx-slack/pull/198))
 
 ## v2.4.0 - 2020-07-30
diff --git a/docs/block-elements.md b/docs/block-elements.md
index cabdb0e..c4b5f95 100644
--- a/docs/block-elements.md
+++ b/docs/block-elements.md
@@ -418,9 +418,9 @@ An easy way to let the user selecting any date is using `<DatePicker>` component
 - `title`/ `hint` (optional): Specify a helpful text appears under the element.
 - `required` (optional): A boolean prop to specify whether any value must be filled when user confirms modal.
 
-### <a name="checkbox-group" id="checkbox-group"></a> [`<CheckboxGroup>`: Checkbox group](https://api.slack.com/reference/block-kit/block-elements#checkboxes) (Only for modal and home tab)
+### <a name="checkbox-group" id="checkbox-group"></a> [`<CheckboxGroup>`: Checkbox group](https://api.slack.com/reference/block-kit/block-elements#checkboxes)
 
-A container for grouping checkboxes. _This component is only for [`<Modal>`](block-containers.md#modal) and [`<Home>`](block-containers.md#home) container. It cannot use in [`<Blocks>`](block-containers.md#blocks) container for messaging._
+A container for grouping checkboxes.
 
 ```jsx
 <Home>
@@ -547,9 +547,9 @@ A below checkbox is meaning exactly the same as an example shown earlier.
 </Checkbox>
 ```
 
-### <a name="radio-button-group" id="radio-button-group"></a> [`<RadioButtonGroup>`: Radio button group](https://api.slack.com/reference/block-kit/block-elements#radio) (Only for modal and home tab)
+### <a name="radio-button-group" id="radio-button-group"></a> [`<RadioButtonGroup>`: Radio button group](https://api.slack.com/reference/block-kit/block-elements#radio)
 
-A container for grouping radio buttons. _This component is only for [`<Modal>`](block-containers.md#modal) and [`<Home>`](block-containers.md#home) container. It cannot use in [`<Blocks>`](block-containers.md#blocks) container for messaging._
+A container for grouping radio buttons.
 
 ```jsx
 <Home>
diff --git a/docs/jsx-components-for-block-kit.md b/docs/jsx-components-for-block-kit.md
index 88acc52..b81d92d 100644
--- a/docs/jsx-components-for-block-kit.md
+++ b/docs/jsx-components-for-block-kit.md
@@ -37,9 +37,9 @@
 - [**`<Overflow>`**: Overflow menu](block-elements.md#overflow)
   - [**`<OverflowItem>`**: Menu item in overflow menu](block-elements.md#overflow-item)
 - [**`<DatePicker>`**: Select date from calendar](block-elements.md#date-picker)
-- [**`<CheckboxGroup>`**: Checkbox group](block-elements.md#checkbox-group) (Only for modal and home tab)
+- [**`<CheckboxGroup>`**: Checkbox group](block-elements.md#checkbox-group)
   - [**`<Checkbox>`**: Checkbox](block-elements.md#checkbox)
-- [**`<RadioButtonGroup>`**: Radio button group](block-elements.md#radio-button-group) (Only for modal and home tab)
+- [**`<RadioButtonGroup>`**: Radio button group](block-elements.md#radio-button-group)
   - [**`<RadioButton>`**: Radio button](block-elements.md#radio-button)
 
 ### **[Composition objects](block-elements.md#composition-objects)**
diff --git a/src/block-kit/container/Blocks.ts b/src/block-kit/container/Blocks.ts
index 6207a49..c68d0e2 100644
--- a/src/block-kit/container/Blocks.ts
+++ b/src/block-kit/container/Blocks.ts
@@ -9,9 +9,6 @@ import {
   generateSectionValidator,
 } from './utils'
 
-// Message block cannot use "radio_buttons" and "checkboxes"
-const blockTypeFilter = (t) => t !== 'radio_buttons' && t !== 'checkboxes'
-
 /**
  * The basic container component for Slack Block Kit suited to
  * {@link https://api.slack.com/surfaces/messages|messages}.
@@ -48,18 +45,14 @@ const blockTypeFilter = (t) => t !== 'radio_buttons' && t !== 'checkboxes'
 export const Blocks = generateBlocksContainer({
   name: 'Blocks',
   availableBlockTypes: {
-    actions: generateActionsValidator(
-      availableActionTypes.filter(blockTypeFilter)
-    ),
+    actions: generateActionsValidator([...availableActionTypes]),
     call: true,
     context: true,
     divider: true,
     file: true,
     header: true,
     image: true,
-    section: generateSectionValidator(
-      availableSectionAccessoryTypes.filter(blockTypeFilter)
-    ),
+    section: generateSectionValidator(availableSectionAccessoryTypes),
   },
   aliases: {
     header: Header,
diff --git a/src/block-kit/elements/CheckboxGroup.ts b/src/block-kit/elements/CheckboxGroup.ts
index 570d94a..b7ff4b1 100644
--- a/src/block-kit/elements/CheckboxGroup.ts
+++ b/src/block-kit/elements/CheckboxGroup.ts
@@ -38,9 +38,6 @@ type CheckboxGroupProps = InputComponentProps<CheckboxGroupBaseProps>
  *
  * Provide the container to choose multiple options supplied by `<Checkbox>`.
  *
- * _This component is available only in `<Modal>` and `<Home>` container, and
- * cannot use in `<Blocks>` container for messaging._
- *
  * @example
  * ```jsx
  * <Modal title="Quick survey">
diff --git a/src/block-kit/elements/RadioButtonGroup.ts b/src/block-kit/elements/RadioButtonGroup.ts
index a5d2a78..8092fe0 100644
--- a/src/block-kit/elements/RadioButtonGroup.ts
+++ b/src/block-kit/elements/RadioButtonGroup.ts
@@ -39,9 +39,6 @@ type RadioButtonGroupProps = InputComponentProps<RadioButtonGroupBaseProps>
  * Provide the container to choose one of the options supplied by
  * `<RadioButton>`.
  *
- * _This component is available only in `<Modal>` and `<Home>` container, and
- * cannot use in `<Blocks>` container for messaging._
- *
  * @example
  * ```jsx
  * <Home>
diff --git a/src/block-kit/layout/Actions.ts b/src/block-kit/layout/Actions.ts
index 0160118..7ad0fad 100644
--- a/src/block-kit/layout/Actions.ts
+++ b/src/block-kit/layout/Actions.ts
@@ -81,8 +81,8 @@ const actionTypeValidators: Record<string, (action: Action) => void> = {
  * - `<ChannelsSelect>` _(Single-select only)_
  * - `<Overflow>`
  * - `<DatePicker>`
- * - `<CheckboxGroup>` _(Only for `<Modal>` and `<Home>` container)_
- * - `<RadioButtonGroup>` _(Only for `<Modal>` and `<Home>` container)_
+ * - `<CheckboxGroup>`
+ * - `<RadioButtonGroup>`
  *
  * @return The partial JSON for `actions` layout block
  */
diff --git a/src/block-kit/layout/Section.ts b/src/block-kit/layout/Section.ts
index 8d2aa70..414d715 100644
--- a/src/block-kit/layout/Section.ts
+++ b/src/block-kit/layout/Section.ts
@@ -143,8 +143,8 @@ export const Field = createComponent<FieldProps, MrkdwnElement>(
  * - `<ChannelsSelect>`
  * - `<Overflow>`
  * - `<DatePicker>`
- * - `<CheckboxGroup>` _(Only for `<Modal>` and `<Home>` container)_
- * - `<RadioButtonGroup>` _(Only for `<Modal>` and `<Home>` container)_
+ * - `<CheckboxGroup>`
+ * - `<RadioButtonGroup>`
  *
  * ### Fields
  *


Test patch:
diff --git a/test/block-kit/block-elements/interactive-components.tsx b/test/block-kit/block-elements/interactive-components.tsx
index 3b094f7..2791ef7 100644
--- a/test/block-kit/block-elements/interactive-components.tsx
+++ b/test/block-kit/block-elements/interactive-components.tsx
@@ -1121,7 +1121,7 @@ describe('Interactive components', () => {
 
       expect(
         JSXSlack(
-          <Home>
+          <Blocks>
             <Actions blockId="actions">
               <RadioButtonGroup actionId="radio-buttons" value="second">
                 <RadioButton value="first" description="The first option">
@@ -1140,8 +1140,8 @@ describe('Interactive components', () => {
                 <RadioButton value="third">3rd</RadioButton>
               </RadioButtonGroup>
             </Actions>
-          </Home>
-        ).blocks
+          </Blocks>
+        )
       ).toStrictEqual([action(radioButtonAction)])
 
       // `checked` state prop, `confirm` prop and HTML-compatible props in <Modal>
@@ -1178,15 +1178,15 @@ describe('Interactive components', () => {
 
     it('outputs radio button group in section block', () => {
       const [section]: SectionBlock[] = JSXSlack(
-        <Home>
+        <Blocks>
           <Section>
             test
             <RadioButtonGroup>
               <RadioButton value="a">A</RadioButton>
             </RadioButtonGroup>
           </Section>
-        </Home>
-      ).blocks
+        </Blocks>
+      )
 
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       expect(section.accessory!.type).toBe('radio_buttons')
@@ -1270,33 +1270,6 @@ describe('Interactive components', () => {
         </RadioButtonGroup>
       )).toThrow(/must contain/i)
     })
-
-    it('throws error when using <RadioButtonGroup> within <Blocks> container', () => {
-      expect(() =>
-        JSXSlack(
-          <Blocks>
-            <Section>
-              test
-              <RadioButtonGroup>
-                <RadioButton value="a">A</RadioButton>
-              </RadioButtonGroup>
-            </Section>
-          </Blocks>
-        )
-      ).toThrow(/incompatible/i)
-
-      expect(() =>
-        JSXSlack(
-          <Blocks>
-            <Actions>
-              <RadioButtonGroup>
-                <RadioButton value="a">A</RadioButton>
-              </RadioButtonGroup>
-            </Actions>
-          </Blocks>
-        )
-      ).toThrow(/incompatible/i)
-    })
   })
 
   describe('<CheckboxGroup>', () => {
@@ -1428,15 +1401,15 @@ describe('Interactive components', () => {
 
     it('outputs checkbox group in section block', () => {
       const [section]: SectionBlock[] = JSXSlack(
-        <Home>
+        <Blocks>
           <Section>
             test
             <CheckboxGroup>
               <Checkbox value="a">A</Checkbox>
             </CheckboxGroup>
           </Section>
-        </Home>
-      ).blocks
+        </Blocks>
+      )
 
       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
       expect(section.accessory!.type).toBe('checkboxes')
@@ -1445,52 +1418,25 @@ describe('Interactive components', () => {
     it('throws error when <CheckboxGroup> has not contained <Checkbox>', () => {
       expect(() =>
         JSXSlack(
-          <Home>
+          <Blocks>
             <Actions>
               <CheckboxGroup>{}</CheckboxGroup>
             </Actions>
-          </Home>
-        )
-      ).toThrow(/must contain/i)
-
-      expect(() =>
-        JSXSlack(
-          <Home>
-            <Actions>
-              <CheckboxGroup>
-                <Option value="wtf">I&apos;m not checkbox</Option>
-              </CheckboxGroup>
-            </Actions>
-          </Home>
-        )
-      ).toThrow(/must contain/i)
-    })
-
-    it('throws error when using <CheckboxGroup> within <Blocks> container', () => {
-      expect(() =>
-        JSXSlack(
-          <Blocks>
-            <Section>
-              test
-              <CheckboxGroup>
-                <Checkbox value="a">A</Checkbox>
-              </CheckboxGroup>
-            </Section>
           </Blocks>
         )
-      ).toThrow(/incompatible/i)
+      ).toThrow(/must contain/i)
 
       expect(() =>
         JSXSlack(
           <Blocks>
             <Actions>
               <CheckboxGroup>
-                <Checkbox value="a">A</Checkbox>
+                <Option value="wtf">I&apos;m not checkbox</Option>
               </CheckboxGroup>
             </Actions>
           </Blocks>
         )
-      ).toThrow(/incompatible/i)
+      ).toThrow(/must contain/i)
     })
 
     it('prefers description prop of <Checkbox> rather than the content in <small> element', () => {
diff --git a/test/block-kit/container-components.tsx b/test/block-kit/container-components.tsx
index ec9e1ea..93caea1 100644
--- a/test/block-kit/container-components.tsx
+++ b/test/block-kit/container-components.tsx
@@ -1,6 +1,7 @@
 /** @jsx JSXSlack.h */
 import { PlainTextElement, View } from '@slack/types'
 import {
+  Actions,
   Blocks,
   Call,
   Escape,
@@ -22,34 +23,52 @@ describe('Container components', () => {
 
   describe('<Blocks>', () => {
     it('throws error when <Blocks> has unexpected element', () => {
-      expect(() =>
-        JSXSlack(
-          <Blocks>
-            <b>unexpected</b>
-          </Blocks>
-        )
-      ).toThrow()
-
-      expect(() =>
-        JSXSlack(
-          <Blocks>
-            <Escape>unexpected</Escape>
-          </Blocks>
-        )
-      ).toThrow()
+      expect(() => (
+        <Blocks>
+          <b>unexpected</b>
+        </Blocks>
+      )).toThrow()
+
+      expect(() => (
+        <Blocks>
+          <Escape>unexpected</Escape>
+        </Blocks>
+      )).toThrow()
 
       // <Input> block cannot use in message
-      expect(() =>
-        JSXSlack(
-          <Blocks>
-            <Input label="Select">
-              <Select>
-                <Option value="test">test</Option>
-              </Select>
-            </Input>
-          </Blocks>
-        )
-      ).toThrow()
+      expect(() => (
+        <Blocks>
+          <Input label="Select">
+            <Select>
+              <Option value="test">test</Option>
+            </Select>
+          </Input>
+        </Blocks>
+      )).toThrow()
+
+      // Incompatible accessory for section block
+      expect(() => (
+        <Blocks>
+          {
+            {
+              type: 'section',
+              accessory: { type: 'incompatible' },
+            } as any
+          }
+        </Blocks>
+      )).toThrow(/incompatible/)
+
+      // Incompatible interactive element for actions block
+      expect(() => (
+        <Blocks>
+          {
+            {
+              type: 'actions',
+              elements: [{ type: 'incompatible' }],
+            } as any
+          }
+        </Blocks>
+      )).toThrow(/incompatible/)
     })
 
     it('ignores invalid literal values to keep compatibillity with v1', () => {