Repository: speee/jsx-slack (ts)

Issue:
<Mrkdwn raw>
`<Mrkdwn raw>` is an idea to provide the solution for #160 right out of the box.

If enabled `raw` attribute, `<Mrkdwn>` won't parse the children as HTML that requires escaping some characters, and set the text to the content of `text` JSON field as is.

```jsx
console.log(
  <Mrkdwn>
    {'Hey <@U024BE7LH>, thanks for submitting your report.'}
  </Mrkdwn>
)
// {
//   "type": "mrkdwn",
//   "text": "Hey &lt;@U024BE7LH&gt;, thanks for submitting your report."
// }

console.log(
  <Mrkdwn raw>
    {'Hey <@U024BE7LH>, thanks for submitting your report.'}
  </Mrkdwn>
)
// {
//   "type": "mrkdwn",
//   "text": "Hey <@U024BE7LH>, thanks for submitting your report."
// }
```

It can avoid escaping `<` and `>` by jsx-slack so would be useful for using the raw mrkdwn text, that is included [advanced formatting with special parsing](https://api.slack.com/reference/surfaces/formatting#advanced).

Pull request description:
Add <Mrkdwn raw> to bypass HTML-like formatting and auto-escaping
`<Mrkdwn raw>` can bypass HTML-like formatting and auto-escaping for [mrkdwn special characters `&`, `<`, and `>`](https://api.slack.com/reference/surfaces/formatting#escaping). It's useful for rendering a raw string that is including [advanced mrkdwn format for Slack](https://api.slack.com/reference/surfaces/formatting#advanced).

Closes #161.

### ToDo

- [x] Update documentation

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a7f3fb3..938e8e4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,7 @@
 ### Added
 
 - [Configurable `dispatchAction` prop](https://github.com/speee/jsx-slack/blob/master/docs/block-elements.md#input) for `<Input type="text">` and `<Textarea>` (equivalent to [`dispatch_action_config` for the plain-text input](https://api.slack.com/reference/block-kit/block-elements#input)) ([#204](https://github.com/speee/jsx-slack/issues/204), [#205](https://github.com/speee/jsx-slack/pull/205))
+- [`<Mrkdwn raw>`](https://github.com/speee/jsx-slack/blob/master/docs/block-elements.md#bypass-html-like-formatting) to bypass HTML-like formatting and auto-escaping ([#161](https://github.com/speee/jsx-slack/issues/161), [#207](https://github.com/speee/jsx-slack/pull/207))
 
 ### Fixed
 
diff --git a/demo/schema.js b/demo/schema.js
index 96c4f34..b9f33bc 100644
--- a/demo/schema.js
+++ b/demo/schema.js
@@ -396,7 +396,7 @@ const schema = {
     children: ['Mrkdwn', ...markupHTML],
   },
   Mrkdwn: {
-    attrs: { verbatim: [] },
+    attrs: { raw: [], verbatim: [] },
     children: markupHTML,
   },
 
diff --git a/docs/block-elements.md b/docs/block-elements.md
index 054a188..9546229 100644
--- a/docs/block-elements.md
+++ b/docs/block-elements.md
@@ -755,9 +755,33 @@ You can use [HTML-like formatting](./html-like-formatting.md) to the content of
 
 ### <a name="mrkdwn" id="mrkdwn"></a> [`<Mrkdwn>`: Text composition object for `mrkdwn` type](https://api.slack.com/reference/block-kit/composition-objects#text)
 
-`<Mrkdwn>`, is a component for text composition object, can use as immediate child of components which support HTML-like formatting. Typically it would be used when needed to set `verbatim` property explicitly.
+Generates text composition object for `mrkdwn` type.
 
-Setting `verbatim` to `false` will tell Slack to auto-convert links, conversation names, and certain mentions to be linkified and automatically parsed. If `verbatim` set to true Slack will skip any preprocessing.
+You can use `<Mrkdwn>` component as immediate child of components which support HTML-like formatting to override mrkdwn text generated by jsx-slack.
+
+#### Bypass HTML-like formatting
+
+[HTML-like formatting](./html-like-formatting.md) is a comfortable way to define Slack Block Kit surfaces with familiar syntaxes, but auto-escape for [mrkdwn special chracters](https://api.slack.com/reference/surfaces/formatting#escaping) may interfere with the completed mrkdwn text.
+
+You can use `<Mrkdwn raw>` if you want to use the raw mrkdwn string as is. It bypasses HTML-like formatting so you cannot use any JSX tags in contents.
+
+```jsx
+<Blocks>
+  <Section>
+    <Mrkdwn raw verbatim>
+      {'Hey <@U0123ABCD>, thanks for submitting your report.'}
+    </Mrkdwn>
+  </Section>
+</Blocks>
+```
+
+[<img src="./preview-btn.svg" width="240" />](https://speee-jsx-slack.netlify.app/#bkb:jsx:eJyzccrJT84utuNSULAJTk0uyczPA7GBPN-i7JTyPIWixHKFstSipMSSzFyIjIKCR2qlglpOibVDqIGhkbGjk7OLWnqJtY5CSUZiXnaxQlp-kUJxaVJuZklJZl66QmV-aZFCUWpBflGJHsRofYjZYEv14bba6EPdAgAgsC25)
+
+In this case, `<@U0123ABCD>` will render as the mention link to specific user.
+
+#### Automatic parsing _(not recommended)_
+
+Setting `verbatim` prop to `false` will tell Slack to auto-convert links, conversation names, and certain mentions to be linkified and automatically parsed. If `verbatim` set to true Slack will skip any preprocessing.
 
 ```jsx
 <Blocks>
@@ -800,7 +824,7 @@ Setting `verbatim` to `false` will tell Slack to auto-convert links, conversatio
 
 <!-- INFO: We have no example in checkbox due to meaningless: Slack will be ignored links and mentions in checkbox. -->
 
-#### Note
+##### Note
 
 _Slack recommends disabling automatic parsing on text composition components and they have made it clear that they might deprecate this feature in the future._ More information can be found [here](https://api.slack.com/reference/surfaces/formatting#why_you_should_consider_disabling_automatic_parsing).
 
@@ -808,7 +832,8 @@ jsx-slack will disable automatic parsing by default even if you were not used `<
 
 #### Props
 
-- `verbatim`: (optional): A boolean prop whether to disable automatic parsing for links, conversation names, and mentions by Slack.
+- `raw` (optional): A boolean value whether to bypass HTML-like formatting and auto-escaping. Any JSX tags cannot use in the content if enabled.
+- `verbatim` (optional): A boolean prop whether to disable automatic parsing for links, conversation names, and mentions by Slack.
 
 ## Input components
 
diff --git a/src/block-kit/composition/Mrkdwn.tsx b/src/block-kit/composition/Mrkdwn.tsx
index 6b9a223..cf05ecc 100644
--- a/src/block-kit/composition/Mrkdwn.tsx
+++ b/src/block-kit/composition/Mrkdwn.tsx
@@ -8,13 +8,37 @@ import {
   isValidElementFromComponent,
 } from '../../jsx'
 import { mrkdwn as toMrkdwn } from '../../mrkdwn/index'
+import { plainText } from './utils'
 
 interface MrkdwnProps {
   children: JSXSlack.ChildElements
 
   /**
-   * A boolean value whether to disable automatic parsing for links,
-   * channel names, and mentions by Slack. Will parse if not defined.
+   * A boolean value whether to bypass HTML-like formatting by jsx-slack.
+   *
+   * If enabled, you can bypass HTML-like formatting, and disable auto escape for
+   * {@link https://api.slack.com/reference/surfaces/formatting#escaping mrkdwn special chracters}.
+   * The defined string in the content will be set as the `text` field of text
+   * composition object as is. Any JSX tags cannot use in contents.
+   *
+   * It's useful for rendering a raw string that is including
+   * {@link https://api.slack.com/reference/surfaces/formatting#advanced advanced mrkdwn format for Slack}.
+   *
+   * ```jsx
+   * <Blocks>
+   *  <Section>
+   *    <Mrkdwn raw verbatim>
+   *      {'Hey <@U0123ABCD>, thanks for submitting your report.'}
+   *    </Mrkdwn>
+   *  </Section>
+   * </Blocks>
+   * ```
+   */
+  raw?: boolean
+
+  /**
+   * A boolean value whether to disable automatic parsing for links, channel
+   * names, and mentions by Slack. Will parse if not defined.
    *
    * @remarks
    * **We recommend always use `<Mrkdwn verbatim>` in your app.**
@@ -33,16 +57,42 @@ const defaultProps = { verbatim: true }
  * for `mrkdwn` type.
  *
  * You should contain the content of the message formatted by HTML-like elements
- * in its children.
+ * in its children (except for `<Mrkdwn raw>`).
  *
  * jsx-slack built-in components that allowed HTML-like elements in children
  * generate an implicit text composition object using `mrkdwn` type with
  * recommended usage by Slack. You can override it by defining `<Mrkdwn>` as an
  * only element of supported components.
  *
- * For example, jsx-slack has disabled automatic parsing of URL, mention, and
- * channel link by Slack to prevent breaking explicitly specified formatting. By
- * using `<Mrkdwn verbatim={false}>`, you can instruct to enable parsing them.
+ * ---
+ *
+ * ### Bypass HTML-like formatting
+ *
+ * HTML-like formatting by jsx-slack is a comfortable way to define Slack Block
+ * Kit surfaces with familiar syntaxes, but auto-escape for
+ * {@link https://api.slack.com/reference/surfaces/formatting#escaping mrkdwn special chracters}
+ * may interfere with the completed mrkdwn text.
+ *
+ * You can use `<Mrkdwn raw>` if you want to use the raw mrkdwn string as is. It
+ * bypasses HTML-like formatting so you cannot use any JSX tags in contents.
+ *
+ * ```jsx
+ * <Blocks>
+ *  <Section>
+ *    <Mrkdwn raw verbatim>
+ *      {'Hey <@U0123ABCD>, thanks for submitting your report.'}
+ *    </Mrkdwn>
+ *  </Section>
+ * </Blocks>
+ * ```
+ *
+ * ---
+ *
+ * ### Automatic parsing _({@link https://api.slack.com/reference/surfaces/formatting#why_you_should_consider_disabling_automatic_parsing not recommended})_
+ *
+ * jsx-slack has disabled automatic parsing of URL, mention, and channel link by
+ * Slack to prevent breaking explicitly specified formatting. By using
+ * `<Mrkdwn verbatim={false}>`, you can instruct to enable parsing them.
  *
  * ```jsx
  * <Blocks>
@@ -88,7 +138,8 @@ const defaultProps = { verbatim: true }
  *
  * @remarks
  * **We recommend never to turn on automatic parsing via
- * `<Mrkdwn verbatim={false}>` in your app.** It's just an escape hatch.
+ * `<Mrkdwn verbatim={false}>` in your app.** It's just an escape hatch, and
+ * probably you always must use `<Mrkdwn verbatim>`.
  *
  * Slack is pointing out it has some possibilities for breaking messages. _Read
  * "{@link https://api.slack.com/reference/surfaces/formatting#why_you_should_consider_disabling_automatic_parsing Why you should consider disabling automatic parsing}"
@@ -100,7 +151,7 @@ export const Mrkdwn = createComponent<MrkdwnProps, MrkdwnElement>(
   'Mrkdwn',
   (props) => ({
     type: 'mrkdwn',
-    text: toMrkdwn(props.children),
+    text: props.raw ? plainText(props.children).text : toMrkdwn(props.children),
     verbatim: props.verbatim,
   })
 )


Test patch:
diff --git a/test/block-kit/block-elements/composition-objects.tsx b/test/block-kit/block-elements/composition-objects.tsx
index f42180d..c76a744 100644
--- a/test/block-kit/block-elements/composition-objects.tsx
+++ b/test/block-kit/block-elements/composition-objects.tsx
@@ -227,5 +227,33 @@ describe('Composition objects', () => {
           ],
         },
       ]))
+
+    it('bypasses HTML-like formatting and auto escape if enabled raw prop', () => {
+      expect(
+        <Mrkdwn raw>{'<!here> test raw string & disabled auto escape'}</Mrkdwn>
+      ).toStrictEqual({
+        type: 'mrkdwn',
+        text: '<!here> test raw string & disabled auto escape',
+      })
+
+      expect(
+        <Mrkdwn raw>
+          ignores <b>bold</b>, <i>italic</i>, and 
strikethrough

+        </Mrkdwn>
+      ).toStrictEqual({
+        type: 'mrkdwn',
+        text: 'ignores bold, italic, and strikethrough',
+      })
+
+      expect(
+        <Mrkdwn raw verbatim>
+          raw &amp; verbatim
+        </Mrkdwn>
+      ).toStrictEqual({
+        type: 'mrkdwn',
+        text: 'raw & verbatim', // &amp; is required to render "&" in JSX
+        verbatim: true,
+      })
+    })
   })
 })