Repository: speee/jsx-slack
Pull request #130 (2020-03-16 01:26:39)

Add value prop to <li>
HTML5 `<li>` element can chang ordered number in the middle of list through [`value` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li#attr-value). To follow this, we've added `value` prop to `<li>` tag.

```jsx
<Blocks>
  <Section>
    <ol>
      <li>1</li>
      <li>2</li>
      <li value={100}>100</li>
      <li>101</li>
      <li>102</li>
    </ol>
  </Section>
</Blocks>
```

[<img src="https://user-images.githubusercontent.com/3993388/76715632-8b57b300-6770-11ea-8d2e-835edf2cf5d4.png" width="212" />](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%E2%80%83%E2%80%84%E2%80%8A1.%201%5Cn%E2%80%83%E2%80%84%E2%80%8A2.%202%5Cn100.%20100%5Cn101.%20101%5Cn102.%20102%22%2C%22verbatim%22%3Atrue%7D%7D%5D)



Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 93c11a1..23f9d4f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 ## [Unreleased]
 
+### Added
+
+- `value` attribute for `<li>` element ([#130](https://github.com/speee/jsx-slack/pull/130))
+
 ### Fixed
 
 - Fix mention detection to match to longer Slack ID ([#129](https://github.com/speee/jsx-slack/pull/129))
diff --git a/demo/schema.js b/demo/schema.js
index 976d888..af1e09e 100644
--- a/demo/schema.js
+++ b/demo/schema.js
@@ -392,7 +392,7 @@ const schema = {
   em: { attrs: {}, children: markupHTML.filter(t => t !== 'i' && t !== 'em') },
   i: { attrs: {}, children: markupHTML.filter(t => t !== 'i' && t !== 'em') },
   li: {
-    attrs: {},
+    attrs: { value: null },
     children: markupHTML.filter(t => t !== 'ul' && t !== 'ol' && t !== 'li'),
   },
   ol: {
diff --git a/docs/html-like-formatting.md b/docs/html-like-formatting.md
index 16ee768..c0855a6 100644
--- a/docs/html-like-formatting.md
+++ b/docs/html-like-formatting.md
@@ -87,7 +87,7 @@ Indents, look like lumpy in a monospace font, will be aligned pretty when render
 
 [<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?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%E2%80%A2%20Item%20A%5Cn%E2%80%A2%20Item%20B%5Cn%E2%80%87%20%E2%97%A6%20Sub%20item%201%5Cn%E2%80%87%20%E2%97%A6%20Sub%20item%202%5Cn%E2%80%87%20%E2%80%84%E2%80%8A%20%E2%96%AA%EF%B8%8E%20and%20more...%5Cn%E2%80%A2%20Item%20C%5Cn%E2%80%87%201.%20Ordered%20item%201%5Cn%E2%80%87%202.%20Ordered%20item%202%5Cn%E2%80%87%20%E2%80%83%E2%80%8A%20%E2%80%87%E2%80%8AI.%20Ordered%20sub%20item%20with%20type%5Cn%E2%80%87%20%E2%80%83%E2%80%8A%20%E2%80%84%E2%80%8AII.%202%5Cn%E2%80%87%20%E2%80%83%E2%80%8A%20%E2%80%8AIII.%203%5Cn%E2%80%87%20%E2%80%83%E2%80%8A%20IV.%204%5Cn%E2%80%87%20%E2%80%83%E2%80%8A%20%E2%80%85V.%205...%22%2C%22verbatim%22%3Atrue%7D%7D%5D)
 
-`<ol>` tag supports [`start` and `type` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol#Attributes) as same as HTML.
+As same as HTML, `<ol>` tag supports [`start` and `type` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/ol#Attributes) and `<li>` tag supports [`value` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/li#Attributes).
 
 ## Links
 
diff --git a/src/jsx.ts b/src/jsx.ts
index a612c72..7725b88 100644
--- a/src/jsx.ts
+++ b/src/jsx.ts
@@ -169,7 +169,7 @@ export namespace JSXSlack {
       i: {}
       img: { alt: string; id?: string; src: string; title?: string }
       input: IntrinsicProps<InputProps>
-      li: {}
+      li: { value?: number; children: Children<any> }
       ol: {
         start?: number
         type?: '1' | 'a' | 'A' | 'i' | 'I'
diff --git a/src/mrkdwn/index.ts b/src/mrkdwn/index.ts
index f7edb31..be4526f 100644
--- a/src/mrkdwn/index.ts
+++ b/src/mrkdwn/index.ts
@@ -1,5 +1,6 @@
 import hast2mdast from 'hast-util-to-mdast'
 import all from 'hast-util-to-mdast/lib/all'
+import listItem from 'hast-util-to-mdast/lib/handlers/list-item'
 import root from 'hast-util-to-mdast/lib/handlers/root'
 import toTextNode from 'hast-util-to-mdast/lib/handlers/textarea'
 import visit from 'unist-util-visit'
@@ -57,6 +58,14 @@ const mrkdwn = (html: string) =>
         }),
         ul: list,
         ol: list,
+        li: (h, node) => {
+          const elm = listItem(h, node)
+          const value = Number.parseInt(node.properties.value, 10)
+
+          if (!Number.isNaN(value)) elm.data = { value }
+
+          return elm
+        },
         span: (h, node) => {
           if (node.properties['data-escape']) {
             return {
diff --git a/src/mrkdwn/jsx.ts b/src/mrkdwn/jsx.ts
index dec7634..ba9c477 100644
--- a/src/mrkdwn/jsx.ts
+++ b/src/mrkdwn/jsx.ts
@@ -106,10 +106,10 @@ const jsxToHtml = (
     case 'small':
     case 'span':
     case 'ul':
-    case 'li':
       return `<${name}>${text()}</${name}>`
     case 'ol':
-      return `<ol${buildAttr(props)}>${text()}</ol>`
+    case 'li':
+      return `<${name}${buildAttr(props)}>${text()}</${name}>`
     default:
       throw new Error(`Unknown HTML-like element: ${name}`)
   }
diff --git a/src/mrkdwn/stringifier.ts b/src/mrkdwn/stringifier.ts
index f98ffb8..0efbba3 100644
--- a/src/mrkdwn/stringifier.ts
+++ b/src/mrkdwn/stringifier.ts
@@ -140,19 +140,22 @@ export class MrkdwnCompiler {
         .replace(/<<ls>>/g, `${makeIndent(maxWidth)} `)
     },
     listItem: node => {
-      let internalNum = 's'
+      let num = 's'
 
       if (!node.data?.implied) {
-        // eslint-disable-next-line no-plusplus
-        const num = ++this.lists[0][0]
-        this.lists[0][1].push(num)
+        if (node.data?.value) {
+          this.lists[0][0] = node.data.value
+        } else {
+          this.lists[0][0] += 1
+        }
 
-        internalNum = num.toString()
+        this.lists[0][1].push(this.lists[0][0])
+        num = this.lists[0][0].toString()
       }
 
       return this.block(node)
         .split('\n')
-        .map((s, i) => `<<l${i > 0 ? 's' : internalNum}>>${s}`)
+        .map((s, i) => `<<l${i > 0 ? 's' : num}>>${s}`)
         .join('\n')
     },
     time: node => {


Test patch:
diff --git a/test/html.tsx b/test/html.tsx
index 24c8f2b..2c95b60 100644
--- a/test/html.tsx
+++ b/test/html.tsx
@@ -890,6 +890,19 @@ describe('HTML parser for mrkdwn', () => {
       )
     })
 
+    it('changes ordered number in the middle of list through value prop', () =>
+      expect(
+        html(
+          <ol>
+            <li>1</li>
+            <li>2</li>
+            <li value={100}>100</li>
+            <li>101</li>
+            <li>102</li>
+          </ol>
+        )
+      ).toBe('   1. 1\n   2. 2\n100. 100\n101. 101\n102. 102'))
+
     it('allows sub list', () => {
       expect(
         html(