Repository: slackapi/python-slack-sdk (python)

Issue:
`RichTextElement.elements` items are never promoted to a proper Python object type
Using the rich text editor widget with the following content:
<img width="477" alt="Screenshot 2024-03-05 at 11 39 10" src="https://github.com/slackapi/python-slack-sdk/assets/118377/0b627c94-4804-4535-8ada-38c4bd91be58">
gets you an unordered list, which upon a `view_submission` event gets posted into the state values as:

<details><summary>JSON representation of state value</summary>

```
{
  "type": "rich_text",
  "elements": [
    {
      "type": "rich_text_list",
      "style": "bullet",
      "indent": 0,
      "elements": [
        {
          "type": "rich_text_section",
          "elements": [
            {
              "type": "text",
              "text": "a"
            }
          ]
        },
        {
          "type": "rich_text_section",
          "elements": [
            {
              "type": "text",
              "text": "b"
            }
          ]
        }
      ],
      "border": 0
    }
  ]
}
```

</details>

which can then be converted into an object like so:
```
>>> obj = Block.parse(example)
>>> type(obj)
slack_sdk.models.blocks.blocks.RichTextBlock
>>> len(obj.elements)
1
>>> type(obj.elements[0])
slack_sdk.models.blocks.block_elements.RichTextListElement
```
which is all well and good... But the child elements of the encountered `RichTextElement` subclass are not converted to the equivalent Python class, and always remain as a dict, effectively looking like the parsing "gave up" at a certain depth (which isn't strictly true):
```
>>> type(obj.elements[0].elements[0])
dict
>>> obj.elements[0].elements[0]
{'type': 'rich_text_section', 'elements': [{'type': 'text', 'text': 'a'}]}
```
As far as I can tell, this is because the `RichTextListElement`, `RichTextPreformattedElement`, `RichTextQuoteElement` and `RichTextSectionElement` never themselves do any parsing, as all of them are defined using:
```python
self.elements = elements
```
instead of something like:
```python
self.elements = BlockElement.parse_all(elements)
```
The latter of which would at least make it so that:
```
>>> type(obj.elements[0].elements[0])
slack_sdk.models.blocks.block_elements.RichTextSectionElement 
```

That still leaves some child elements unpromoted, because there's nothing which converts `{'type': 'text', 'text': 'a'}` to a `RichTextElementParts.Text` etc, but that feels like a somewhat separate issue.

### Category
- [x] **slack_sdk.models** (UI component builders)


Pull request:
fix `RichTextElement.elements` items are never promoted to a proper Python object type
## Summary

This pull request resolves #1468 

### Category (place an `x` in each of the `[ ]`)

- [ ] **slack_sdk.web.WebClient (sync/async)** (Web API client)
- [ ] **slack_sdk.webhook.WebhookClient (sync/async)** (Incoming Webhook, response_url sender)
- [ ] **slack_sdk.socket_mode** (Socket Mode client)
- [ ] **slack_sdk.signature** (Request Signature Verifier)
- [ ] **slack_sdk.oauth** (OAuth Flow Utilities)
- [x] **slack_sdk.models** (UI component builders)
- [ ] **slack_sdk.scim** (SCIM API client)
- [ ] **slack_sdk.audit_logs** (Audit Logs API client)
- [ ] **slack_sdk.rtm_v2** (RTM client)
- [ ] `/docs-src` (Documents, have you run `./scripts/docs.sh`?)
- [ ] `/docs-src-v2` (Documents, have you run `./scripts/docs-v2.sh`?)
- [ ] `/tutorial` (PythOnBoardingBot tutorial)
- [ ] `tests`/`integration_tests` (Automated tests for this library)

## Requirements (place an `x` in each `[ ]`)

- [x] I've read and understood the [Contributing Guidelines](https://github.com/slackapi/python-slack-sdk/blob/main/.github/contributing.md) and have done my best effort to follow them.
- [x] I've read and agree to the [Code of Conduct](https://slackhq.github.io/code-of-conduct).
- [x] I've run `python3 -m venv .venv && source .venv/bin/activate && ./scripts/run_validation.sh` after making the changes.


Patch:
diff --git a/slack_sdk/models/blocks/block_elements.py b/slack_sdk/models/blocks/block_elements.py
index 8977001..46ab75d 100644
--- a/slack_sdk/models/blocks/block_elements.py
+++ b/slack_sdk/models/blocks/block_elements.py
@@ -1868,7 +1868,7 @@ class RichTextListElement(RichTextElement):
     ):
         super().__init__(type=self.type)
         show_unknown_key_warning(self, others)
-        self.elements = elements
+        self.elements = BlockElement.parse_all(elements)
         self.style = style
         self.indent = indent
         self.offset = offset
@@ -1891,7 +1891,7 @@ class RichTextPreformattedElement(RichTextElement):
     ):
         super().__init__(type=self.type)
         show_unknown_key_warning(self, others)
-        self.elements = elements
+        self.elements = BlockElement.parse_all(elements)
         self.border = border
 
 
@@ -1910,7 +1910,7 @@ class RichTextQuoteElement(RichTextElement):
     ):
         super().__init__(type=self.type)
         show_unknown_key_warning(self, others)
-        self.elements = elements
+        self.elements = BlockElement.parse_all(elements)
 
 
 class RichTextSectionElement(RichTextElement):
@@ -1928,7 +1928,7 @@ class RichTextSectionElement(RichTextElement):
     ):
         super().__init__(type=self.type)
         show_unknown_key_warning(self, others)
-        self.elements = elements
+        self.elements = BlockElement.parse_all(elements)
 
 
 class RichTextElementParts:


Test patch:
diff --git a/tests/slack_sdk/models/test_blocks.py b/tests/slack_sdk/models/test_blocks.py
index 92abc76..66b17d2 100644
--- a/tests/slack_sdk/models/test_blocks.py
+++ b/tests/slack_sdk/models/test_blocks.py
@@ -1083,3 +1083,38 @@ class RichTextBlockTests(unittest.TestCase):
             ],
         )
         self.assertDictEqual(dict_block, class_block.to_dict())
+
+    def test_elements_are_parsed(self):
+        dict_block = {
+            "type": "rich_text",
+            "elements": [
+                {
+                    "type": "rich_text_section",
+                    "elements": [{"type": "text", "text": "Hello there, I am a basic rich text block!"}],
+                },
+                {
+                    "type": "rich_text_quote",
+                    "elements": [{"type": "text", "text": "this is very important"}],
+                },
+                {
+                    "type": "rich_text_preformatted",
+                    "elements": [{"type": "text", "text": 'print("Hello world")'}],
+                },
+                {
+                    "type": "rich_text_list",
+                    "elements": [
+                        {"type": "rich_text_section", "elements": [{"type": "text", "text": "a"}]},
+                    ],
+                },
+            ],
+        }
+        block = RichTextBlock(**dict_block)
+        self.assertIsInstance(block.elements[0], RichTextSectionElement)
+        self.assertIsInstance(block.elements[0].elements[0], RichTextElementParts.Text)
+        self.assertIsInstance(block.elements[1], RichTextQuoteElement)
+        self.assertIsInstance(block.elements[1].elements[0], RichTextElementParts.Text)
+        self.assertIsInstance(block.elements[2], RichTextPreformattedElement)
+        self.assertIsInstance(block.elements[2].elements[0], RichTextElementParts.Text)
+        self.assertIsInstance(block.elements[3], RichTextListElement)
+        self.assertIsInstance(block.elements[3].elements[0], RichTextSectionElement)
+        self.assertIsInstance(block.elements[3].elements[0].elements[0], RichTextElementParts.Text)