Repository: slackapi/python-slack-sdk
Pull request #876 (2020-11-12 07:52:09)

Add timepicker block element support
## Summary

This pull request adds `timepicker` block element support. As the element is still in beta, the specification can be changed until its GA release.

### Category (place an `x` in each of the `[ ]`)

- [ ] **slack_sdk.web.WebClient** (Web API client)
- [ ] **slack_sdk.webhook.WebhookClient** (Incoming Webhook, response_url sender)
- [x] **slack_sdk.models** (UI component builders)
- [ ] **slack_sdk.oauth** (OAuth Flow Utilities)
- [ ] **slack_sdk.rtm.RTMClient** (RTM client)
- [ ] **slack_sdk.signature** (Request Signature Verifier)
- [ ] `/docs-src` (Documents, have you run `./docs.sh`?)
- [ ] `/docs-src-v2` (Documents, have you run `./docs-v2.sh`?)

## 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 `python setup.py validate` after making the changes.


codecov[bot]: # [Codecov](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=h1) Report
> Merging [#876](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=desc) (96f0a09) into [main](https://codecov.io/gh/slackapi/python-slack-sdk/commit/6339fbe81031c9aec3f95927ac03706fd31f3544?el=desc) (6339fbe) will **increase** coverage by `0.09%`.
> The diff coverage is `100.00%`.

[![Impacted file tree graph](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876/graphs/tree.svg?width=650&height=150&src=pr&token=F0O7meYmtD)](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##             main     #876      +/-   ##
==========================================
+ Coverage   76.28%   76.38%   +0.09%     
==========================================
  Files          35       35              
  Lines        3226     3239      +13     
==========================================
+ Hits         2461     2474      +13     
  Misses        765      765              
```


| [Impacted Files](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [slack\_sdk/models/blocks/\_\_init\_\_.py](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876/diff?src=pr&el=tree#diff-c2xhY2tfc2RrL21vZGVscy9ibG9ja3MvX19pbml0X18ucHk=) | `100.00% <100.00%> (ø)` | |
| [slack\_sdk/models/blocks/block\_elements.py](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876/diff?src=pr&el=tree#diff-c2xhY2tfc2RrL21vZGVscy9ibG9ja3MvYmxvY2tfZWxlbWVudHMucHk=) | `92.32% <100.00%> (+0.22%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=footer). Last update [6339fbe...96f0a09](https://codecov.io/gh/slackapi/python-slack-sdk/pull/876?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

noqcks: Hi! Is this PR waiting for TimePicker to be GA? Is there anything blocking its release?
seratch: @noqcks 
>Is this PR waiting for TimePicker to be GA?

Yes, it is! If you need this right now, please do copy & paste for now.
CLAassistant: [![CLA assistant check](https://cla-assistant.io/pull/badge/not_signed)](https://cla-assistant.io/slackapi/python-slack-sdk?pullRequest=876) <br/>Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our [Contributor License Agreement](https://cla-assistant.io/slackapi/python-slack-sdk?pullRequest=876) before we can accept your contribution.<br/><sub>You have signed the CLA already but the status is still pending? Let us [recheck](https://cla-assistant.io/check/slackapi/python-slack-sdk?pullRequest=876) it.</sub>
seratch: As I mentioned at https://github.com/slackapi/node-slack-sdk/pull/1226#issuecomment-833326483 , we are going to merge this change into the next minor version release. I will update the commit and make this PR ready fro review soon.

Patch:
diff --git a/slack_sdk/models/blocks/__init__.py b/slack_sdk/models/blocks/__init__.py
index 1944aa5..5272c27 100644
--- a/slack_sdk/models/blocks/__init__.py
+++ b/slack_sdk/models/blocks/__init__.py
@@ -23,6 +23,7 @@ from .block_elements import ConversationFilter  # noqa
 from .block_elements import ConversationMultiSelectElement  # noqa
 from .block_elements import ConversationSelectElement  # noqa
 from .block_elements import DatePickerElement  # noqa
+from .block_elements import TimePickerElement  # noqa
 from .block_elements import ExternalDataMultiSelectElement  # noqa
 from .block_elements import ExternalDataSelectElement  # noqa
 from .block_elements import ImageElement  # noqa
diff --git a/slack_sdk/models/blocks/block_elements.py b/slack_sdk/models/blocks/block_elements.py
index f328973..4f0489c 100644
--- a/slack_sdk/models/blocks/block_elements.py
+++ b/slack_sdk/models/blocks/block_elements.py
@@ -377,6 +377,48 @@ class DatePickerElement(InputInteractiveElement):
         )
 
 
+# -------------------------------------------------
+# TimePicker
+# -------------------------------------------------
+
+
+class TimePickerElement(InputInteractiveElement):
+    type = "timepicker"
+
+    @property
+    def attributes(self) -> Set[str]:
+        return super().attributes.union({"initial_time"})
+
+    def __init__(
+        self,
+        *,
+        action_id: Optional[str] = None,
+        placeholder: Optional[Union[str, dict, TextObject]] = None,
+        initial_time: Optional[str] = None,
+        confirm: Optional[Union[dict, ConfirmObject]] = None,
+        **others: dict,
+    ):
+        """
+        An element which allows selection of a time of day.
+        https://api.slack.com/reference/block-kit/block-elements#timepicker
+        """
+        super().__init__(
+            type=self.type,
+            action_id=action_id,
+            placeholder=TextObject.parse(placeholder, PlainTextObject.type),
+            confirm=ConfirmObject.parse(confirm),
+        )
+        show_unknown_key_warning(self, others)
+
+        self.initial_time = initial_time
+
+    @JsonValidator("initial_time attribute must be in format 'HH:mm'")
+    def _validate_initial_time_valid(self):
+        return self.initial_time is None or re.match(
+            r"([0-1][0-9]|2[0-3]):([0-5][0-9])", self.initial_time
+        )
+
+
 # -------------------------------------------------
 # Image
 # -------------------------------------------------


Test patch:
diff --git a/tests/slack_sdk/models/test_elements.py b/tests/slack_sdk/models/test_elements.py
index f34584e..63573c9 100644
--- a/tests/slack_sdk/models/test_elements.py
+++ b/tests/slack_sdk/models/test_elements.py
@@ -4,6 +4,7 @@ from slack_sdk.errors import SlackObjectFormationError
 from slack_sdk.models.blocks import (
     ButtonElement,
     DatePickerElement,
+    TimePickerElement,
     ExternalDataSelectElement,
     ImageElement,
     LinkButtonElement,
@@ -216,6 +217,51 @@ class DatePickerElementTests(unittest.TestCase):
             elem.to_dict()
 
 
+# -------------------------------------------------
+# TimePicker
+# -------------------------------------------------
+
+
+class TimePickerElementTests(unittest.TestCase):
+    def test_document(self):
+        input = {
+            "type": "timepicker",
+            "action_id": "timepicker123",
+            "initial_time": "11:40",
+            "placeholder": {"type": "plain_text", "text": "Select a time",},
+        }
+        self.assertDictEqual(input, TimePickerElement(**input).to_dict())
+
+    def test_json(self):
+        for hour in range(0, 23):
+            for minute in range(0, 59):
+                time = f"{hour:02}:{minute:02}"
+                self.assertDictEqual(
+                    {
+                        "action_id": "timepicker123",
+                        "initial_time": time,
+                        "placeholder": {
+                            "emoji": True,
+                            "type": "plain_text",
+                            "text": "Select a time",
+                        },
+                        "type": "timepicker",
+                    },
+                    TimePickerElement(
+                        action_id="timepicker123",
+                        placeholder="Select a time",
+                        initial_time=time,
+                    ).to_dict(),
+                )
+
+        with self.assertRaises(SlackObjectFormationError):
+            TimePickerElement(
+                action_id="timepicker123",
+                placeholder="Select a time",
+                initial_time="25:00",
+            ).to_dict()
+
+
 # -------------------------------------------------
 # Image
 # -------------------------------------------------