From 099fd695a5cbe3fef2332d850f84df0acb236110 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 27 Aug 2026 21:58:52 -0700 Subject: [PATCH 1/2] feat(block-kit): add block element examples Add examples for the block elements that the SDK already implements, each in its own file under block-kit/src/block_elements/ with a matching test. Every example mirrors the JSON payload(s) from the element's docs.slack.dev reference page verbatim -- hosted in a valid block (or input) where the element requires one -- so each is a runnable payload rather than a bare element that cannot be sent on its own. Where a reference page documents several payloads, every one is exemplified (for instance the select and multi-select menus each cover all five source variants, and the image element covers image_url and both slack_file forms). Tests assert the complete reference JSON. Covered: button, checkboxes, date picker, datetime picker, email input, file input, image, multi-select menu, number input, overflow menu, plain-text input, radio button group, select menu, time picker, URL input, and workflow button. Descriptions match the docs copy verbatim, following the existing blocks/ convention. The button page also documents an agent_prompt variant (Slackbot hand-off) that the SDK does not yet implement -- deferred here. This PR is the home for block elements the SDK implements; elements not yet implemented in the SDK are a separate follow-up. Co-Authored-By: Claude --- block-kit/README.md | 19 ++++ block-kit/src/block_elements/__init__.py | 0 block-kit/src/block_elements/button.py | 76 +++++++++++++ block-kit/src/block_elements/checkboxes.py | 36 ++++++ block-kit/src/block_elements/date_picker.py | 22 ++++ .../src/block_elements/datetime_picker.py | 18 +++ block-kit/src/block_elements/email_input.py | 21 ++++ block-kit/src/block_elements/file_input.py | 22 ++++ block-kit/src/block_elements/image.py | 62 ++++++++++ .../src/block_elements/multi_select_menu.py | 106 ++++++++++++++++++ block-kit/src/block_elements/number_input.py | 20 ++++ block-kit/src/block_elements/overflow_menu.py | 46 ++++++++ .../src/block_elements/plain_text_input.py | 17 +++ block-kit/src/block_elements/radio_buttons.py | 24 ++++ block-kit/src/block_elements/select_menu.py | 106 ++++++++++++++++++ block-kit/src/block_elements/time_picker.py | 23 ++++ block-kit/src/block_elements/url_input.py | 17 +++ .../src/block_elements/workflow_button.py | 42 +++++++ block-kit/tests/block_elements/__init__.py | 0 block-kit/tests/block_elements/test_button.py | 77 +++++++++++++ .../tests/block_elements/test_checkboxes.py | 31 +++++ .../tests/block_elements/test_date_picker.py | 20 ++++ .../block_elements/test_datetime_picker.py | 15 +++ .../tests/block_elements/test_email_input.py | 19 ++++ .../tests/block_elements/test_file_input.py | 20 ++++ block-kit/tests/block_elements/test_image.py | 62 ++++++++++ .../block_elements/test_multi_select_menu.py | 98 ++++++++++++++++ .../tests/block_elements/test_number_input.py | 18 +++ .../block_elements/test_overflow_menu.py | 43 +++++++ .../block_elements/test_plain_text_input.py | 17 +++ .../block_elements/test_radio_buttons.py | 25 +++++ .../tests/block_elements/test_select_menu.py | 101 +++++++++++++++++ .../tests/block_elements/test_time_picker.py | 21 ++++ .../tests/block_elements/test_url_input.py | 14 +++ .../block_elements/test_workflow_button.py | 36 ++++++ 35 files changed, 1294 insertions(+) create mode 100644 block-kit/src/block_elements/__init__.py create mode 100644 block-kit/src/block_elements/button.py create mode 100644 block-kit/src/block_elements/checkboxes.py create mode 100644 block-kit/src/block_elements/date_picker.py create mode 100644 block-kit/src/block_elements/datetime_picker.py create mode 100644 block-kit/src/block_elements/email_input.py create mode 100644 block-kit/src/block_elements/file_input.py create mode 100644 block-kit/src/block_elements/image.py create mode 100644 block-kit/src/block_elements/multi_select_menu.py create mode 100644 block-kit/src/block_elements/number_input.py create mode 100644 block-kit/src/block_elements/overflow_menu.py create mode 100644 block-kit/src/block_elements/plain_text_input.py create mode 100644 block-kit/src/block_elements/radio_buttons.py create mode 100644 block-kit/src/block_elements/select_menu.py create mode 100644 block-kit/src/block_elements/time_picker.py create mode 100644 block-kit/src/block_elements/url_input.py create mode 100644 block-kit/src/block_elements/workflow_button.py create mode 100644 block-kit/tests/block_elements/__init__.py create mode 100644 block-kit/tests/block_elements/test_button.py create mode 100644 block-kit/tests/block_elements/test_checkboxes.py create mode 100644 block-kit/tests/block_elements/test_date_picker.py create mode 100644 block-kit/tests/block_elements/test_datetime_picker.py create mode 100644 block-kit/tests/block_elements/test_email_input.py create mode 100644 block-kit/tests/block_elements/test_file_input.py create mode 100644 block-kit/tests/block_elements/test_image.py create mode 100644 block-kit/tests/block_elements/test_multi_select_menu.py create mode 100644 block-kit/tests/block_elements/test_number_input.py create mode 100644 block-kit/tests/block_elements/test_overflow_menu.py create mode 100644 block-kit/tests/block_elements/test_plain_text_input.py create mode 100644 block-kit/tests/block_elements/test_radio_buttons.py create mode 100644 block-kit/tests/block_elements/test_select_menu.py create mode 100644 block-kit/tests/block_elements/test_time_picker.py create mode 100644 block-kit/tests/block_elements/test_url_input.py create mode 100644 block-kit/tests/block_elements/test_workflow_button.py diff --git a/block-kit/README.md b/block-kit/README.md index 9785597..14fb3fa 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -24,3 +24,22 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes - **[Table](https://docs.slack.dev/reference/block-kit/blocks/table-block)**: Displays structured information in a table. [Implementation](./src/blocks/table.py). - **[Task card](https://docs.slack.dev/reference/block-kit/blocks/task-card-block)**: Displays a single task, representing a single action. [Implementation](./src/blocks/task_card.py). - **[Video](https://docs.slack.dev/reference/block-kit/blocks/video-block)**: Displays an embedded video player. [Implementation](./src/blocks/video.py). + +### Block elements + +- **[Button](https://docs.slack.dev/reference/block-kit/block-elements/button-element)**: Allows users a direct path to performing basic actions. [Implementation](./src/block_elements/button.py). +- **[Checkboxes](https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element)**: Allows users to choose multiple items from a list of options. [Implementation](./src/block_elements/checkboxes.py). +- **[Date picker](https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element)**: Allows users to select a date from a calendar style UI. [Implementation](./src/block_elements/date_picker.py). +- **[Datetime picker](https://docs.slack.dev/reference/block-kit/block-elements/datetime-picker-element)**: Allows users to select both a date and a time of day. [Implementation](./src/block_elements/datetime_picker.py). +- **[Email input](https://docs.slack.dev/reference/block-kit/block-elements/email-input-element)**: Allows user to enter an email into a single-line field. [Implementation](./src/block_elements/email_input.py). +- **[File input](https://docs.slack.dev/reference/block-kit/block-elements/file-input-element)**: Allows user to upload files. [Implementation](./src/block_elements/file_input.py). +- **[Image](https://docs.slack.dev/reference/block-kit/block-elements/image-element)**: Displays an image as part of a larger block of content. [Implementation](./src/block_elements/image.py). +- **[Multi-select menu](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element)**: Allows users to select multiple items from a list of options. [Implementation](./src/block_elements/multi_select_menu.py). +- **[Number input](https://docs.slack.dev/reference/block-kit/block-elements/number-input-element)**: Allows user to enter a number into a single-line field. [Implementation](./src/block_elements/number_input.py). +- **[Overflow menu](https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element)**: Allows users to press a button to view a list of options. [Implementation](./src/block_elements/overflow_menu.py). +- **[Plain-text input](https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element)**: Allows users to enter freeform text data into a single-line or multi-line field. [Implementation](./src/block_elements/plain_text_input.py). +- **[Radio button group](https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element)**: Allows users to choose one item from a list of possible options. [Implementation](./src/block_elements/radio_buttons.py). +- **[Select menu](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element)**: Allows users to choose an option from a drop down menu. [Implementation](./src/block_elements/select_menu.py). +- **[Time picker](https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element)**: Allows users to select a time of day. [Implementation](./src/block_elements/time_picker.py). +- **[URL input](https://docs.slack.dev/reference/block-kit/block-elements/url-input-element)**: Allows user to enter a URL into a single-line field. [Implementation](./src/block_elements/url_input.py). +- **[Workflow button](https://docs.slack.dev/reference/block-kit/block-elements/workflow-button-element)**: Allows users to run a link trigger with customizable inputs. [Implementation](./src/block_elements/workflow_button.py). diff --git a/block-kit/src/block_elements/__init__.py b/block-kit/src/block_elements/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/block-kit/src/block_elements/button.py b/block-kit/src/block_elements/button.py new file mode 100644 index 0000000..692602a --- /dev/null +++ b/block-kit/src/block_elements/button.py @@ -0,0 +1,76 @@ +from slack_sdk.models.blocks import ActionsBlock, Block, SectionBlock +from slack_sdk.models.blocks.basic_components import MarkdownTextObject, PlainTextObject +from slack_sdk.models.blocks.block_elements import ButtonElement + + +def example01() -> ButtonElement: + """ + Allows users a direct path to performing basic actions. + https://docs.slack.dev/reference/block-kit/block-elements/button-element/ + + A regular interactive button. + """ + element = ButtonElement( + text=PlainTextObject(text="Click Me"), + value="click_me_123", + action_id="button", + ) + return element + + +def example02() -> ButtonElement: + """ + A button with a primary style attribute. + """ + element = ButtonElement( + text=PlainTextObject(text="Save"), + style="primary", + value="click_me_123", + action_id="button", + ) + return element + + +def example03() -> ButtonElement: + """ + A link button. + """ + element = ButtonElement( + text=PlainTextObject(text="Link Button"), + url="https://docs.slack.dev/block-kit", + ) + return element + + +def example04() -> list[Block]: + """ + The button element must be used inside either the section or actions block. + + A section block with a button as an accessory, followed by an actions + block holding a primary button and a link button. + """ + blocks: list[Block] = [ + SectionBlock( + text=MarkdownTextObject(text="This is a section block with a button."), + accessory=ButtonElement( + text=PlainTextObject(text="Click Me"), + value="click_me_123", + action_id="button", + ), + ), + ActionsBlock( + block_id="actionblock789", + elements=[ + ButtonElement( + text=PlainTextObject(text="Primary Button"), + style="primary", + value="click_me_456", + ), + ButtonElement( + text=PlainTextObject(text="Link Button"), + url="https://api.slack.com/block-kit", + ), + ], + ), + ] + return blocks diff --git a/block-kit/src/block_elements/checkboxes.py b/block-kit/src/block_elements/checkboxes.py new file mode 100644 index 0000000..80e7625 --- /dev/null +++ b/block-kit/src/block_elements/checkboxes.py @@ -0,0 +1,36 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import ( + MarkdownTextObject, + Option, + PlainTextObject, +) +from slack_sdk.models.blocks.block_elements import CheckboxesElement + + +def example01() -> SectionBlock: + """ + Allows users to choose multiple items from a list of options. + https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element/ + + A section block with a checkboxes element as an accessory. + """ + block = SectionBlock( + text=PlainTextObject(text="Check out these charming checkboxes"), + accessory=CheckboxesElement( + action_id="this_is_an_action_id", + initial_options=[ + Option(value="A1", text=PlainTextObject(text="Checkbox 1")), + ], + options=[ + Option(value="A1", text=PlainTextObject(text="Checkbox 1")), + Option( + value="A2", + text=PlainTextObject(text="Checkbox 2"), + description=MarkdownTextObject( + text="*A description of option two*" + ), + ), + ], + ), + ) + return block diff --git a/block-kit/src/block_elements/date_picker.py b/block-kit/src/block_elements/date_picker.py new file mode 100644 index 0000000..53277dd --- /dev/null +++ b/block-kit/src/block_elements/date_picker.py @@ -0,0 +1,22 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import MarkdownTextObject, PlainTextObject +from slack_sdk.models.blocks.block_elements import DatePickerElement + + +def example01() -> SectionBlock: + """ + Allows users to select a date from a calendar style UI. + https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element/ + + A section block with a date picker as an accessory. + """ + block = SectionBlock( + block_id="section1234", + text=MarkdownTextObject(text="Pick a date for the deadline."), + accessory=DatePickerElement( + action_id="datepicker123", + initial_date="1990-04-28", + placeholder=PlainTextObject(text="Select a date"), + ), + ) + return block diff --git a/block-kit/src/block_elements/datetime_picker.py b/block-kit/src/block_elements/datetime_picker.py new file mode 100644 index 0000000..ab6c6ec --- /dev/null +++ b/block-kit/src/block_elements/datetime_picker.py @@ -0,0 +1,18 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import DateTimePickerElement + + +def example01() -> InputBlock: + """ + Allows users to select both a date and a time of day. + https://docs.slack.dev/reference/block-kit/block-elements/datetime-picker-element/ + + An input block with a datetime picker element. + """ + block = InputBlock( + element=DateTimePickerElement(action_id="datetimepicker-action"), + hint=PlainTextObject(text="This is some hint text", emoji=True), + label=PlainTextObject(text="Start date", emoji=True), + ) + return block diff --git a/block-kit/src/block_elements/email_input.py b/block-kit/src/block_elements/email_input.py new file mode 100644 index 0000000..6747117 --- /dev/null +++ b/block-kit/src/block_elements/email_input.py @@ -0,0 +1,21 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import EmailInputElement + + +def example01() -> InputBlock: + """ + Allows user to enter an email into a single-line field. + https://docs.slack.dev/reference/block-kit/block-elements/email-input-element/ + + An input block with an email input element. + """ + block = InputBlock( + block_id="input123", + label=PlainTextObject(text="Email Address"), + element=EmailInputElement( + action_id="email_text_input-action", + placeholder=PlainTextObject(text="Enter an email"), + ), + ) + return block diff --git a/block-kit/src/block_elements/file_input.py b/block-kit/src/block_elements/file_input.py new file mode 100644 index 0000000..2468c2c --- /dev/null +++ b/block-kit/src/block_elements/file_input.py @@ -0,0 +1,22 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import FileInputElement + + +def example01() -> InputBlock: + """ + Allows user to upload files. + https://docs.slack.dev/reference/block-kit/block-elements/file-input-element/ + + An input block with a file input element. + """ + block = InputBlock( + block_id="input_block_id", + label=PlainTextObject(text="Upload Files"), + element=FileInputElement( + action_id="file_input_action_id_1", + filetypes=["jpg", "png"], + max_files=5, + ), + ) + return block diff --git a/block-kit/src/block_elements/image.py b/block-kit/src/block_elements/image.py new file mode 100644 index 0000000..ac91113 --- /dev/null +++ b/block-kit/src/block_elements/image.py @@ -0,0 +1,62 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import ( + MarkdownTextObject, + SlackFile, +) +from slack_sdk.models.blocks.block_elements import ImageElement + + +def example01() -> SectionBlock: + """ + Displays an image as part of a larger block of content. + https://docs.slack.dev/reference/block-kit/block-elements/image-element/ + + A section block with an accessory image referenced by URL. + """ + block = SectionBlock( + block_id="section567", + text=MarkdownTextObject( + text="This is a section block with an accessory image." + ), + accessory=ImageElement( + image_url="https://pbs.twimg.com/profile_images/625633822235693056/lNGUneLX_400x400.jpg", + alt_text="cute cat", + ), + ) + return block + + +def example02() -> SectionBlock: + """ + A section block with an accessory image referenced by a Slack file URL. + """ + block = SectionBlock( + block_id="section567", + text=MarkdownTextObject( + text="This is a section block with an accessory image." + ), + accessory=ImageElement( + slack_file=SlackFile( + url="https://files.slack.com/files-pri/T0123456-F0123456/xyz.png" + ), + alt_text="Slack file object.", + ), + ) + return block + + +def example03() -> SectionBlock: + """ + A section block with an accessory image referenced by a Slack file ID. + """ + block = SectionBlock( + block_id="section567", + text=MarkdownTextObject( + text="This is a section block with an accessory image." + ), + accessory=ImageElement( + slack_file=SlackFile(id="F01234567"), + alt_text="Slack file object.", + ), + ) + return block diff --git a/block-kit/src/block_elements/multi_select_menu.py b/block-kit/src/block_elements/multi_select_menu.py new file mode 100644 index 0000000..8059ab5 --- /dev/null +++ b/block-kit/src/block_elements/multi_select_menu.py @@ -0,0 +1,106 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import ( + MarkdownTextObject, + Option, + PlainTextObject, +) +from slack_sdk.models.blocks.block_elements import ( + ChannelMultiSelectElement, + ConversationMultiSelectElement, + ExternalDataMultiSelectElement, + StaticMultiSelectElement, + UserMultiSelectElement, +) + + +def example01() -> SectionBlock: + """ + Allows users to select multiple items from a list of options. + https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element/ + + A section block with a static multi-select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick items from the list"), + accessory=StaticMultiSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select items"), + options=[ + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-0", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-1", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-2", + ), + ], + ), + ) + return block + + +def example02() -> SectionBlock: + """ + A section block with an external data source multi-select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick items from the list"), + accessory=ExternalDataMultiSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select items"), + min_query_length=3, + ), + ) + return block + + +def example03() -> SectionBlock: + """ + A section block with a user list multi-select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick users from the list"), + accessory=UserMultiSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select users"), + ), + ) + return block + + +def example04() -> SectionBlock: + """ + A section block with a conversations list multi-select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick conversations from the list"), + accessory=ConversationMultiSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select conversations"), + ), + ) + return block + + +def example05() -> SectionBlock: + """ + A section block with a channels list multi-select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick channels from the list"), + accessory=ChannelMultiSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select channels"), + ), + ) + return block diff --git a/block-kit/src/block_elements/number_input.py b/block-kit/src/block_elements/number_input.py new file mode 100644 index 0000000..383af09 --- /dev/null +++ b/block-kit/src/block_elements/number_input.py @@ -0,0 +1,20 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import NumberInputElement + + +def example01() -> InputBlock: + """ + Allows user to enter a number into a single-line field. + https://docs.slack.dev/reference/block-kit/block-elements/number-input-element/ + + An input block with a number input element. + """ + block = InputBlock( + element=NumberInputElement( + is_decimal_allowed=False, + action_id="number_input-action", + ), + label=PlainTextObject(text="Label", emoji=True), + ) + return block diff --git a/block-kit/src/block_elements/overflow_menu.py b/block-kit/src/block_elements/overflow_menu.py new file mode 100644 index 0000000..524d524 --- /dev/null +++ b/block-kit/src/block_elements/overflow_menu.py @@ -0,0 +1,46 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import ( + MarkdownTextObject, + Option, + PlainTextObject, +) +from slack_sdk.models.blocks.block_elements import OverflowMenuElement + + +def example01() -> SectionBlock: + """ + Allows users to press a button to view a list of options. + https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element/ + + A section block with an overflow menu as an accessory. + """ + block = SectionBlock( + block_id="section 890", + text=MarkdownTextObject(text="This is a section block with an overflow menu."), + accessory=OverflowMenuElement( + action_id="overflow", + options=[ + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-0", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-1", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-2", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-3", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-4", + ), + ], + ), + ) + return block diff --git a/block-kit/src/block_elements/plain_text_input.py b/block-kit/src/block_elements/plain_text_input.py new file mode 100644 index 0000000..102b9f8 --- /dev/null +++ b/block-kit/src/block_elements/plain_text_input.py @@ -0,0 +1,17 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import PlainTextInputElement + + +def example01() -> InputBlock: + """ + Allows users to enter freeform text data into a single-line or multi-line field. + https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element/ + + An input block containing a plain-text input element. + """ + block = InputBlock( + element=PlainTextInputElement(action_id="plain_text_input-action"), + label=PlainTextObject(text="Label", emoji=True), + ) + return block diff --git a/block-kit/src/block_elements/radio_buttons.py b/block-kit/src/block_elements/radio_buttons.py new file mode 100644 index 0000000..6334ae1 --- /dev/null +++ b/block-kit/src/block_elements/radio_buttons.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import Option, PlainTextObject +from slack_sdk.models.blocks.block_elements import RadioButtonsElement + + +def example01() -> SectionBlock: + """ + Allows users to choose one item from a list of possible options. + https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element/ + + A section block with a radio button group element as an accessory. + """ + block = SectionBlock( + text=PlainTextObject(text="Check out these rad radio buttons"), + accessory=RadioButtonsElement( + action_id="this_is_an_action_id", + initial_option=Option(value="A1", text=PlainTextObject(text="Radio 1")), + options=[ + Option(value="A1", text=PlainTextObject(text="Radio 1")), + Option(value="A2", text=PlainTextObject(text="Radio 2")), + ], + ), + ) + return block diff --git a/block-kit/src/block_elements/select_menu.py b/block-kit/src/block_elements/select_menu.py new file mode 100644 index 0000000..e45a735 --- /dev/null +++ b/block-kit/src/block_elements/select_menu.py @@ -0,0 +1,106 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import ( + MarkdownTextObject, + Option, + PlainTextObject, +) +from slack_sdk.models.blocks.block_elements import ( + ChannelSelectElement, + ConversationSelectElement, + ExternalDataSelectElement, + StaticSelectElement, + UserSelectElement, +) + + +def example01() -> SectionBlock: + """ + Allows users to choose an option from a drop down menu. + https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element/ + + A section block with a static select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick an item from the dropdown list"), + accessory=StaticSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select an item"), + options=[ + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-0", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-1", + ), + Option( + text=PlainTextObject(text="*this is plain_text text*"), + value="value-2", + ), + ], + ), + ) + return block + + +def example02() -> SectionBlock: + """ + A section block with an external data source select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick an item from the dropdown list"), + accessory=ExternalDataSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select an item"), + min_query_length=3, + ), + ) + return block + + +def example03() -> SectionBlock: + """ + A section block with a user list select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick a user from the dropdown list"), + accessory=UserSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select an item"), + ), + ) + return block + + +def example04() -> SectionBlock: + """ + A section block with a conversations list select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick a conversation from the dropdown list"), + accessory=ConversationSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select an item"), + ), + ) + return block + + +def example05() -> SectionBlock: + """ + A section block with a channels list select menu as an accessory. + """ + block = SectionBlock( + block_id="section678", + text=MarkdownTextObject(text="Pick a channel from the dropdown list"), + accessory=ChannelSelectElement( + action_id="text1234", + placeholder=PlainTextObject(text="Select an item"), + ), + ) + return block diff --git a/block-kit/src/block_elements/time_picker.py b/block-kit/src/block_elements/time_picker.py new file mode 100644 index 0000000..874d0dc --- /dev/null +++ b/block-kit/src/block_elements/time_picker.py @@ -0,0 +1,23 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import MarkdownTextObject, PlainTextObject +from slack_sdk.models.blocks.block_elements import TimePickerElement + + +def example01() -> SectionBlock: + """ + Allows users to select a time of day. + https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element/ + + A section block with a time picker as an accessory. + """ + block = SectionBlock( + block_id="section1234", + text=MarkdownTextObject(text="Pick a date for the deadline."), + accessory=TimePickerElement( + timezone="America/Los_Angeles", + action_id="timepicker123", + initial_time="11:40", + placeholder=PlainTextObject(text="Select a time"), + ), + ) + return block diff --git a/block-kit/src/block_elements/url_input.py b/block-kit/src/block_elements/url_input.py new file mode 100644 index 0000000..cb0702d --- /dev/null +++ b/block-kit/src/block_elements/url_input.py @@ -0,0 +1,17 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import UrlInputElement + + +def example01() -> InputBlock: + """ + Allows user to enter a URL into a single-line field. + https://docs.slack.dev/reference/block-kit/block-elements/url-input-element/ + + An input block with a URL input element. + """ + block = InputBlock( + element=UrlInputElement(action_id="url_text_input-action"), + label=PlainTextObject(text="Label", emoji=True), + ) + return block diff --git a/block-kit/src/block_elements/workflow_button.py b/block-kit/src/block_elements/workflow_button.py new file mode 100644 index 0000000..c054a0d --- /dev/null +++ b/block-kit/src/block_elements/workflow_button.py @@ -0,0 +1,42 @@ +from slack_sdk.models.blocks import SectionBlock +from slack_sdk.models.blocks.basic_components import ( + MarkdownTextObject, + PlainTextObject, + Workflow, + WorkflowTrigger, +) +from slack_sdk.models.blocks.block_elements import WorkflowButtonElement + + +def example01() -> SectionBlock: + """ + Allows users to run a link trigger with customizable inputs. + https://docs.slack.dev/reference/block-kit/block-elements/workflow-button-element/ + + A section block with a workflow button as an accessory. + """ + block = SectionBlock( + text=MarkdownTextObject( + text="A message *with some bold text* and _some italicized text_." + ), + accessory=WorkflowButtonElement( + text=PlainTextObject(text="Run Workflow"), + action_id="workflowbutton123", + workflow=Workflow( + trigger=WorkflowTrigger( + url="https://slack.com/shortcuts/Ft0123ABC456/xyz...zyx", + customizable_input_parameters=[ + { + "name": "input_parameter_a", + "value": "Value for input param A", + }, + { + "name": "input_parameter_b", + "value": "Value for input param B", + }, + ], + ), + ), + ), + ) + return block diff --git a/block-kit/tests/block_elements/__init__.py b/block-kit/tests/block_elements/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/block-kit/tests/block_elements/test_button.py b/block-kit/tests/block_elements/test_button.py new file mode 100644 index 0000000..f484188 --- /dev/null +++ b/block-kit/tests/block_elements/test_button.py @@ -0,0 +1,77 @@ +import json + +from src.block_elements import button + + +def test_example01(): + element = button.example01() + actual = element.to_dict() + expected = { + "type": "button", + "text": {"type": "plain_text", "text": "Click Me"}, + "value": "click_me_123", + "action_id": "button", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + element = button.example02() + actual = element.to_dict() + expected = { + "type": "button", + "text": {"type": "plain_text", "text": "Save"}, + "style": "primary", + "value": "click_me_123", + "action_id": "button", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + element = button.example03() + actual = element.to_dict() + expected = { + "type": "button", + "text": {"type": "plain_text", "text": "Link Button"}, + "url": "https://docs.slack.dev/block-kit", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example04(): + blocks = button.example04() + actual = [block.to_dict() for block in blocks] + expected = [ + { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "This is a section block with a button.", + }, + "accessory": { + "type": "button", + "text": {"type": "plain_text", "text": "Click Me"}, + "value": "click_me_123", + "action_id": "button", + }, + }, + { + "type": "actions", + "block_id": "actionblock789", + "elements": [ + { + "type": "button", + "text": {"type": "plain_text", "text": "Primary Button"}, + "style": "primary", + "value": "click_me_456", + }, + { + "type": "button", + "text": {"type": "plain_text", "text": "Link Button"}, + "url": "https://api.slack.com/block-kit", + }, + ], + }, + ] + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_checkboxes.py b/block-kit/tests/block_elements/test_checkboxes.py new file mode 100644 index 0000000..db1f8b7 --- /dev/null +++ b/block-kit/tests/block_elements/test_checkboxes.py @@ -0,0 +1,31 @@ +import json + +from src.block_elements import checkboxes + + +def test_example01(): + block = checkboxes.example01() + actual = block.to_dict() + expected = { + "type": "section", + "text": {"type": "plain_text", "text": "Check out these charming checkboxes"}, + "accessory": { + "type": "checkboxes", + "action_id": "this_is_an_action_id", + "initial_options": [ + {"value": "A1", "text": {"type": "plain_text", "text": "Checkbox 1"}}, + ], + "options": [ + {"value": "A1", "text": {"type": "plain_text", "text": "Checkbox 1"}}, + { + "value": "A2", + "text": {"type": "plain_text", "text": "Checkbox 2"}, + "description": { + "type": "mrkdwn", + "text": "*A description of option two*", + }, + }, + ], + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_date_picker.py b/block-kit/tests/block_elements/test_date_picker.py new file mode 100644 index 0000000..9fb73ab --- /dev/null +++ b/block-kit/tests/block_elements/test_date_picker.py @@ -0,0 +1,20 @@ +import json + +from src.block_elements import date_picker + + +def test_example01(): + block = date_picker.example01() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section1234", + "text": {"type": "mrkdwn", "text": "Pick a date for the deadline."}, + "accessory": { + "type": "datepicker", + "action_id": "datepicker123", + "initial_date": "1990-04-28", + "placeholder": {"type": "plain_text", "text": "Select a date"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_datetime_picker.py b/block-kit/tests/block_elements/test_datetime_picker.py new file mode 100644 index 0000000..4a3ce3b --- /dev/null +++ b/block-kit/tests/block_elements/test_datetime_picker.py @@ -0,0 +1,15 @@ +import json + +from src.block_elements import datetime_picker + + +def test_example01(): + block = datetime_picker.example01() + actual = block.to_dict() + expected = { + "type": "input", + "element": {"type": "datetimepicker", "action_id": "datetimepicker-action"}, + "hint": {"type": "plain_text", "text": "This is some hint text", "emoji": True}, + "label": {"type": "plain_text", "text": "Start date", "emoji": True}, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_email_input.py b/block-kit/tests/block_elements/test_email_input.py new file mode 100644 index 0000000..c9ececc --- /dev/null +++ b/block-kit/tests/block_elements/test_email_input.py @@ -0,0 +1,19 @@ +import json + +from src.block_elements import email_input + + +def test_example01(): + block = email_input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "block_id": "input123", + "label": {"type": "plain_text", "text": "Email Address"}, + "element": { + "type": "email_text_input", + "action_id": "email_text_input-action", + "placeholder": {"type": "plain_text", "text": "Enter an email"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_file_input.py b/block-kit/tests/block_elements/test_file_input.py new file mode 100644 index 0000000..97cc1d4 --- /dev/null +++ b/block-kit/tests/block_elements/test_file_input.py @@ -0,0 +1,20 @@ +import json + +from src.block_elements import file_input + + +def test_example01(): + block = file_input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "block_id": "input_block_id", + "label": {"type": "plain_text", "text": "Upload Files"}, + "element": { + "type": "file_input", + "action_id": "file_input_action_id_1", + "filetypes": ["jpg", "png"], + "max_files": 5, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_image.py b/block-kit/tests/block_elements/test_image.py new file mode 100644 index 0000000..5d5693e --- /dev/null +++ b/block-kit/tests/block_elements/test_image.py @@ -0,0 +1,62 @@ +import json + +from src.block_elements import image + + +def test_example01(): + block = image.example01() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section567", + "text": { + "type": "mrkdwn", + "text": "This is a section block with an accessory image.", + }, + "accessory": { + "type": "image", + "image_url": "https://pbs.twimg.com/profile_images/625633822235693056/lNGUneLX_400x400.jpg", + "alt_text": "cute cat", + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = image.example02() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section567", + "text": { + "type": "mrkdwn", + "text": "This is a section block with an accessory image.", + }, + "accessory": { + "type": "image", + "slack_file": { + "url": "https://files.slack.com/files-pri/T0123456-F0123456/xyz.png" + }, + "alt_text": "Slack file object.", + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + block = image.example03() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section567", + "text": { + "type": "mrkdwn", + "text": "This is a section block with an accessory image.", + }, + "accessory": { + "type": "image", + "slack_file": {"id": "F01234567"}, + "alt_text": "Slack file object.", + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_multi_select_menu.py b/block-kit/tests/block_elements/test_multi_select_menu.py new file mode 100644 index 0000000..83670d9 --- /dev/null +++ b/block-kit/tests/block_elements/test_multi_select_menu.py @@ -0,0 +1,98 @@ +import json + +from src.block_elements import multi_select_menu + + +def test_example01(): + block = multi_select_menu.example01() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick items from the list"}, + "accessory": { + "type": "multi_static_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select items"}, + "options": [ + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-0", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-1", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-2", + }, + ], + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = multi_select_menu.example02() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick items from the list"}, + "accessory": { + "type": "multi_external_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select items"}, + "min_query_length": 3, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + block = multi_select_menu.example03() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick users from the list"}, + "accessory": { + "type": "multi_users_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select users"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example04(): + block = multi_select_menu.example04() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick conversations from the list"}, + "accessory": { + "type": "multi_conversations_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select conversations"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example05(): + block = multi_select_menu.example05() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick channels from the list"}, + "accessory": { + "type": "multi_channels_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select channels"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_number_input.py b/block-kit/tests/block_elements/test_number_input.py new file mode 100644 index 0000000..c20ded6 --- /dev/null +++ b/block-kit/tests/block_elements/test_number_input.py @@ -0,0 +1,18 @@ +import json + +from src.block_elements import number_input + + +def test_example01(): + block = number_input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "element": { + "type": "number_input", + "is_decimal_allowed": False, + "action_id": "number_input-action", + }, + "label": {"type": "plain_text", "text": "Label", "emoji": True}, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_overflow_menu.py b/block-kit/tests/block_elements/test_overflow_menu.py new file mode 100644 index 0000000..2aed0b5 --- /dev/null +++ b/block-kit/tests/block_elements/test_overflow_menu.py @@ -0,0 +1,43 @@ +import json + +from src.block_elements import overflow_menu + + +def test_example01(): + block = overflow_menu.example01() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section 890", + "text": { + "type": "mrkdwn", + "text": "This is a section block with an overflow menu.", + }, + "accessory": { + "type": "overflow", + "action_id": "overflow", + "options": [ + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-0", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-1", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-2", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-3", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-4", + }, + ], + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_plain_text_input.py b/block-kit/tests/block_elements/test_plain_text_input.py new file mode 100644 index 0000000..d7b110d --- /dev/null +++ b/block-kit/tests/block_elements/test_plain_text_input.py @@ -0,0 +1,17 @@ +import json + +from src.block_elements import plain_text_input + + +def test_example01(): + block = plain_text_input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "element": { + "type": "plain_text_input", + "action_id": "plain_text_input-action", + }, + "label": {"type": "plain_text", "text": "Label", "emoji": True}, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_radio_buttons.py b/block-kit/tests/block_elements/test_radio_buttons.py new file mode 100644 index 0000000..46f5c11 --- /dev/null +++ b/block-kit/tests/block_elements/test_radio_buttons.py @@ -0,0 +1,25 @@ +import json + +from src.block_elements import radio_buttons + + +def test_example01(): + block = radio_buttons.example01() + actual = block.to_dict() + expected = { + "type": "section", + "text": {"type": "plain_text", "text": "Check out these rad radio buttons"}, + "accessory": { + "type": "radio_buttons", + "action_id": "this_is_an_action_id", + "initial_option": { + "value": "A1", + "text": {"type": "plain_text", "text": "Radio 1"}, + }, + "options": [ + {"value": "A1", "text": {"type": "plain_text", "text": "Radio 1"}}, + {"value": "A2", "text": {"type": "plain_text", "text": "Radio 2"}}, + ], + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_select_menu.py b/block-kit/tests/block_elements/test_select_menu.py new file mode 100644 index 0000000..19c0128 --- /dev/null +++ b/block-kit/tests/block_elements/test_select_menu.py @@ -0,0 +1,101 @@ +import json + +from src.block_elements import select_menu + + +def test_example01(): + block = select_menu.example01() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick an item from the dropdown list"}, + "accessory": { + "type": "static_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select an item"}, + "options": [ + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-0", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-1", + }, + { + "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "value": "value-2", + }, + ], + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = select_menu.example02() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick an item from the dropdown list"}, + "accessory": { + "type": "external_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select an item"}, + "min_query_length": 3, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example03(): + block = select_menu.example03() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick a user from the dropdown list"}, + "accessory": { + "type": "users_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select an item"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example04(): + block = select_menu.example04() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": { + "type": "mrkdwn", + "text": "Pick a conversation from the dropdown list", + }, + "accessory": { + "type": "conversations_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select an item"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example05(): + block = select_menu.example05() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section678", + "text": {"type": "mrkdwn", "text": "Pick a channel from the dropdown list"}, + "accessory": { + "type": "channels_select", + "action_id": "text1234", + "placeholder": {"type": "plain_text", "text": "Select an item"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_time_picker.py b/block-kit/tests/block_elements/test_time_picker.py new file mode 100644 index 0000000..81bf035 --- /dev/null +++ b/block-kit/tests/block_elements/test_time_picker.py @@ -0,0 +1,21 @@ +import json + +from src.block_elements import time_picker + + +def test_example01(): + block = time_picker.example01() + actual = block.to_dict() + expected = { + "type": "section", + "block_id": "section1234", + "text": {"type": "mrkdwn", "text": "Pick a date for the deadline."}, + "accessory": { + "type": "timepicker", + "timezone": "America/Los_Angeles", + "action_id": "timepicker123", + "initial_time": "11:40", + "placeholder": {"type": "plain_text", "text": "Select a time"}, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_url_input.py b/block-kit/tests/block_elements/test_url_input.py new file mode 100644 index 0000000..7b6e858 --- /dev/null +++ b/block-kit/tests/block_elements/test_url_input.py @@ -0,0 +1,14 @@ +import json + +from src.block_elements import url_input + + +def test_example01(): + block = url_input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "element": {"type": "url_text_input", "action_id": "url_text_input-action"}, + "label": {"type": "plain_text", "text": "Label", "emoji": True}, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_workflow_button.py b/block-kit/tests/block_elements/test_workflow_button.py new file mode 100644 index 0000000..58f3be2 --- /dev/null +++ b/block-kit/tests/block_elements/test_workflow_button.py @@ -0,0 +1,36 @@ +import json + +from src.block_elements import workflow_button + + +def test_example01(): + block = workflow_button.example01() + actual = block.to_dict() + expected = { + "type": "section", + "text": { + "type": "mrkdwn", + "text": "A message *with some bold text* and _some italicized text_.", + }, + "accessory": { + "type": "workflow_button", + "text": {"type": "plain_text", "text": "Run Workflow"}, + "action_id": "workflowbutton123", + "workflow": { + "trigger": { + "url": "https://slack.com/shortcuts/Ft0123ABC456/xyz...zyx", + "customizable_input_parameters": [ + { + "name": "input_parameter_a", + "value": "Value for input param A", + }, + { + "name": "input_parameter_b", + "value": "Value for input param B", + }, + ], + } + }, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) From ab097478b316a12b9c9869dbc1dcc3d52b6d07c1 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Thu, 27 Aug 2026 23:33:10 -0700 Subject: [PATCH 2/2] Add rich text, feedback, and URL source block element examples Adds examples for documented block elements the palette was missing: the rich-text sub-elements (emoji, broadcast, color, date, link, text, channel, user, usergroup, team), the rich-text containers (section, list, quote, preformatted), the rich-text input, the context_actions elements (feedback buttons, icon button), and the URL source. Each ships a matching test asserting the complete reference JSON from docs.slack.dev, plus an alphabetical README entry. Also reformats every existing block_elements test's expected payload to one key per line with a magic trailing comma so ruff format keeps them exploded. Co-Authored-By: Claude --- block-kit/README.md | 18 +++ block-kit/src/block_elements/broadcast.py | 24 +++ block-kit/src/block_elements/channel.py | 24 +++ block-kit/src/block_elements/color.py | 24 +++ block-kit/src/block_elements/date.py | 28 ++++ block-kit/src/block_elements/emoji.py | 28 ++++ .../src/block_elements/feedback_buttons.py | 33 ++++ block-kit/src/block_elements/icon_button.py | 23 +++ block-kit/src/block_elements/link.py | 24 +++ .../src/block_elements/rich_text_input.py | 24 +++ .../src/block_elements/rich_text_list.py | 95 ++++++++++++ .../block_elements/rich_text_preformatted.py | 27 ++++ .../src/block_elements/rich_text_quote.py | 33 ++++ .../src/block_elements/rich_text_section.py | 67 ++++++++ block-kit/src/block_elements/team.py | 24 +++ block-kit/src/block_elements/text.py | 28 ++++ block-kit/src/block_elements/url_source.py | 38 +++++ block-kit/src/block_elements/user.py | 24 +++ block-kit/src/block_elements/usergroup.py | 24 +++ .../tests/block_elements/test_broadcast.py | 23 +++ block-kit/tests/block_elements/test_button.py | 30 +++- .../tests/block_elements/test_channel.py | 23 +++ .../tests/block_elements/test_checkboxes.py | 26 +++- block-kit/tests/block_elements/test_color.py | 23 +++ block-kit/tests/block_elements/test_date.py | 25 +++ .../tests/block_elements/test_date_picker.py | 10 +- .../block_elements/test_datetime_picker.py | 17 ++- .../tests/block_elements/test_email_input.py | 10 +- block-kit/tests/block_elements/test_emoji.py | 39 +++++ .../block_elements/test_feedback_buttons.py | 34 +++++ .../tests/block_elements/test_file_input.py | 10 +- .../tests/block_elements/test_icon_button.py | 24 +++ block-kit/tests/block_elements/test_image.py | 6 +- block-kit/tests/block_elements/test_link.py | 23 +++ .../block_elements/test_multi_select_menu.py | 65 ++++++-- .../tests/block_elements/test_number_input.py | 6 +- .../block_elements/test_overflow_menu.py | 25 ++- .../block_elements/test_plain_text_input.py | 6 +- .../block_elements/test_radio_buttons.py | 26 +++- .../block_elements/test_rich_text_input.py | 31 ++++ .../block_elements/test_rich_text_list.py | 144 ++++++++++++++++++ .../test_rich_text_preformatted.py | 24 +++ .../block_elements/test_rich_text_quote.py | 33 ++++ .../block_elements/test_rich_text_section.py | 88 +++++++++++ .../tests/block_elements/test_select_menu.py | 60 ++++++-- block-kit/tests/block_elements/test_team.py | 23 +++ block-kit/tests/block_elements/test_text.py | 30 ++++ .../tests/block_elements/test_time_picker.py | 10 +- .../tests/block_elements/test_url_input.py | 11 +- .../tests/block_elements/test_url_source.py | 38 +++++ block-kit/tests/block_elements/test_user.py | 23 +++ .../tests/block_elements/test_usergroup.py | 23 +++ .../block_elements/test_workflow_button.py | 7 +- 53 files changed, 1543 insertions(+), 63 deletions(-) create mode 100644 block-kit/src/block_elements/broadcast.py create mode 100644 block-kit/src/block_elements/channel.py create mode 100644 block-kit/src/block_elements/color.py create mode 100644 block-kit/src/block_elements/date.py create mode 100644 block-kit/src/block_elements/emoji.py create mode 100644 block-kit/src/block_elements/feedback_buttons.py create mode 100644 block-kit/src/block_elements/icon_button.py create mode 100644 block-kit/src/block_elements/link.py create mode 100644 block-kit/src/block_elements/rich_text_input.py create mode 100644 block-kit/src/block_elements/rich_text_list.py create mode 100644 block-kit/src/block_elements/rich_text_preformatted.py create mode 100644 block-kit/src/block_elements/rich_text_quote.py create mode 100644 block-kit/src/block_elements/rich_text_section.py create mode 100644 block-kit/src/block_elements/team.py create mode 100644 block-kit/src/block_elements/text.py create mode 100644 block-kit/src/block_elements/url_source.py create mode 100644 block-kit/src/block_elements/user.py create mode 100644 block-kit/src/block_elements/usergroup.py create mode 100644 block-kit/tests/block_elements/test_broadcast.py create mode 100644 block-kit/tests/block_elements/test_channel.py create mode 100644 block-kit/tests/block_elements/test_color.py create mode 100644 block-kit/tests/block_elements/test_date.py create mode 100644 block-kit/tests/block_elements/test_emoji.py create mode 100644 block-kit/tests/block_elements/test_feedback_buttons.py create mode 100644 block-kit/tests/block_elements/test_icon_button.py create mode 100644 block-kit/tests/block_elements/test_link.py create mode 100644 block-kit/tests/block_elements/test_rich_text_input.py create mode 100644 block-kit/tests/block_elements/test_rich_text_list.py create mode 100644 block-kit/tests/block_elements/test_rich_text_preformatted.py create mode 100644 block-kit/tests/block_elements/test_rich_text_quote.py create mode 100644 block-kit/tests/block_elements/test_rich_text_section.py create mode 100644 block-kit/tests/block_elements/test_team.py create mode 100644 block-kit/tests/block_elements/test_text.py create mode 100644 block-kit/tests/block_elements/test_url_source.py create mode 100644 block-kit/tests/block_elements/test_user.py create mode 100644 block-kit/tests/block_elements/test_usergroup.py diff --git a/block-kit/README.md b/block-kit/README.md index 14fb3fa..ecc536c 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -27,19 +27,37 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes ### Block elements +- **[Broadcast](https://docs.slack.dev/reference/block-kit/block-elements/broadcast-element)**: Displays a broadcast mention such as here, channel, or everyone. [Implementation](./src/block_elements/broadcast.py). - **[Button](https://docs.slack.dev/reference/block-kit/block-elements/button-element)**: Allows users a direct path to performing basic actions. [Implementation](./src/block_elements/button.py). +- **[Channel](https://docs.slack.dev/reference/block-kit/block-elements/channel-element)**: Renders as a mention of a channel. [Implementation](./src/block_elements/channel.py). - **[Checkboxes](https://docs.slack.dev/reference/block-kit/block-elements/checkboxes-element)**: Allows users to choose multiple items from a list of options. [Implementation](./src/block_elements/checkboxes.py). +- **[Color](https://docs.slack.dev/reference/block-kit/block-elements/color-element)**: Displays a color swatch from a hex value. [Implementation](./src/block_elements/color.py). +- **[Date](https://docs.slack.dev/reference/block-kit/block-elements/date-element)**: Displays a formatted, localized date. [Implementation](./src/block_elements/date.py). - **[Date picker](https://docs.slack.dev/reference/block-kit/block-elements/date-picker-element)**: Allows users to select a date from a calendar style UI. [Implementation](./src/block_elements/date_picker.py). - **[Datetime picker](https://docs.slack.dev/reference/block-kit/block-elements/datetime-picker-element)**: Allows users to select both a date and a time of day. [Implementation](./src/block_elements/datetime_picker.py). - **[Email input](https://docs.slack.dev/reference/block-kit/block-elements/email-input-element)**: Allows user to enter an email into a single-line field. [Implementation](./src/block_elements/email_input.py). +- **[Emoji](https://docs.slack.dev/reference/block-kit/block-elements/emoji-element)**: Displays an emoji. [Implementation](./src/block_elements/emoji.py). +- **[Feedback buttons](https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element)**: Buttons to indicate positive or negative feedback. [Implementation](./src/block_elements/feedback_buttons.py). - **[File input](https://docs.slack.dev/reference/block-kit/block-elements/file-input-element)**: Allows user to upload files. [Implementation](./src/block_elements/file_input.py). +- **[Icon button](https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element)**: An icon button to perform actions. [Implementation](./src/block_elements/icon_button.py). - **[Image](https://docs.slack.dev/reference/block-kit/block-elements/image-element)**: Displays an image as part of a larger block of content. [Implementation](./src/block_elements/image.py). +- **[Link](https://docs.slack.dev/reference/block-kit/block-elements/link-element)**: Displays a hyperlink. [Implementation](./src/block_elements/link.py). - **[Multi-select menu](https://docs.slack.dev/reference/block-kit/block-elements/multi-select-menu-element)**: Allows users to select multiple items from a list of options. [Implementation](./src/block_elements/multi_select_menu.py). - **[Number input](https://docs.slack.dev/reference/block-kit/block-elements/number-input-element)**: Allows user to enter a number into a single-line field. [Implementation](./src/block_elements/number_input.py). - **[Overflow menu](https://docs.slack.dev/reference/block-kit/block-elements/overflow-menu-element)**: Allows users to press a button to view a list of options. [Implementation](./src/block_elements/overflow_menu.py). - **[Plain-text input](https://docs.slack.dev/reference/block-kit/block-elements/plain-text-input-element)**: Allows users to enter freeform text data into a single-line or multi-line field. [Implementation](./src/block_elements/plain_text_input.py). - **[Radio button group](https://docs.slack.dev/reference/block-kit/block-elements/radio-button-group-element)**: Allows users to choose one item from a list of possible options. [Implementation](./src/block_elements/radio_buttons.py). +- **[Rich text input](https://docs.slack.dev/reference/block-kit/block-elements/rich-text-input-element)**: Allows users to enter formatted text in a WYSIWYG composer, offering the same messaging writing experience as in Slack. [Implementation](./src/block_elements/rich_text_input.py). +- **[Rich text list](https://docs.slack.dev/reference/block-kit/block-elements/rich-text-list-element)**: Displays a list of rich text items. [Implementation](./src/block_elements/rich_text_list.py). +- **[Rich text preformatted](https://docs.slack.dev/reference/block-kit/block-elements/rich-text-preformatted-element)**: Displays a preformatted rich text element. [Implementation](./src/block_elements/rich_text_preformatted.py). +- **[Rich text quote](https://docs.slack.dev/reference/block-kit/block-elements/rich-text-quote-element)**: Displays a rich text quote block. [Implementation](./src/block_elements/rich_text_quote.py). +- **[Rich text section](https://docs.slack.dev/reference/block-kit/block-elements/rich-text-section-element)**: A section element that holds rich text elements. [Implementation](./src/block_elements/rich_text_section.py). - **[Select menu](https://docs.slack.dev/reference/block-kit/block-elements/select-menu-element)**: Allows users to choose an option from a drop down menu. [Implementation](./src/block_elements/select_menu.py). +- **[Team](https://docs.slack.dev/reference/block-kit/block-elements/team-element)**: Renders as a mention of a workspace or team. [Implementation](./src/block_elements/team.py). +- **[Text](https://docs.slack.dev/reference/block-kit/block-elements/text-element)**: Displays text, optionally with styling. [Implementation](./src/block_elements/text.py). - **[Time picker](https://docs.slack.dev/reference/block-kit/block-elements/time-picker-element)**: Allows users to select a time of day. [Implementation](./src/block_elements/time_picker.py). - **[URL input](https://docs.slack.dev/reference/block-kit/block-elements/url-input-element)**: Allows user to enter a URL into a single-line field. [Implementation](./src/block_elements/url_input.py). +- **[URL source](https://docs.slack.dev/reference/block-kit/block-elements/url-source-element)**: Displays a URL source for referencing within a task card block. [Implementation](./src/block_elements/url_source.py). +- **[User](https://docs.slack.dev/reference/block-kit/block-elements/user-element)**: Renders as a mention of a user. [Implementation](./src/block_elements/user.py). +- **[Usergroup](https://docs.slack.dev/reference/block-kit/block-elements/usergroup-element)**: Renders as a mention of a user group. [Implementation](./src/block_elements/usergroup.py). - **[Workflow button](https://docs.slack.dev/reference/block-kit/block-elements/workflow-button-element)**: Allows users to run a link trigger with customizable inputs. [Implementation](./src/block_elements/workflow_button.py). diff --git a/block-kit/src/block_elements/broadcast.py b/block-kit/src/block_elements/broadcast.py new file mode 100644 index 0000000..9dfff17 --- /dev/null +++ b/block-kit/src/block_elements/broadcast.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a broadcast mention such as here, channel, or everyone. + https://docs.slack.dev/reference/block-kit/block-elements/broadcast-element/ + + A rich text block whose section holds a broadcast element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Broadcast(range="everyone"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/channel.py b/block-kit/src/block_elements/channel.py new file mode 100644 index 0000000..7928700 --- /dev/null +++ b/block-kit/src/block_elements/channel.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Renders as a mention of a channel. + https://docs.slack.dev/reference/block-kit/block-elements/channel-element/ + + A rich text block whose section holds a channel mention element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Channel(channel_id="C123ABC456"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/color.py b/block-kit/src/block_elements/color.py new file mode 100644 index 0000000..2e69ca4 --- /dev/null +++ b/block-kit/src/block_elements/color.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a color swatch from a hex value. + https://docs.slack.dev/reference/block-kit/block-elements/color-element/ + + A rich text block whose section holds a color element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Color(value="#F405B3"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/date.py b/block-kit/src/block_elements/date.py new file mode 100644 index 0000000..510d2b7 --- /dev/null +++ b/block-kit/src/block_elements/date.py @@ -0,0 +1,28 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a formatted, localized date. + https://docs.slack.dev/reference/block-kit/block-elements/date-element/ + + A rich text block whose section holds a date element with a fallback. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Date( + timestamp=1720710212, + format="{date_num} at {time}", + fallback="timey", + ), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/emoji.py b/block-kit/src/block_elements/emoji.py new file mode 100644 index 0000000..6f5a134 --- /dev/null +++ b/block-kit/src/block_elements/emoji.py @@ -0,0 +1,28 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays an emoji. + https://docs.slack.dev/reference/block-kit/block-elements/emoji-element/ + + A rich text block whose section holds emoji elements separated by text. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Emoji(name="basketball"), + RichTextElementParts.Text(text=" "), + RichTextElementParts.Emoji(name="snowboarder"), + RichTextElementParts.Text(text=" "), + RichTextElementParts.Emoji(name="checkered_flag"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/feedback_buttons.py b/block-kit/src/block_elements/feedback_buttons.py new file mode 100644 index 0000000..200d3db --- /dev/null +++ b/block-kit/src/block_elements/feedback_buttons.py @@ -0,0 +1,33 @@ +from slack_sdk.models.blocks import ContextActionsBlock +from slack_sdk.models.blocks.basic_components import ( + FeedbackButtonObject, + PlainTextObject, +) +from slack_sdk.models.blocks.block_elements import FeedbackButtonsElement + + +def example01() -> ContextActionsBlock: + """ + Buttons to indicate positive or negative feedback. + https://docs.slack.dev/reference/block-kit/block-elements/feedback-buttons-element/ + + A context actions block holding a feedback buttons element. + """ + block = ContextActionsBlock( + elements=[ + FeedbackButtonsElement( + action_id="feedback_buttons_1", + positive_button=FeedbackButtonObject( + text=PlainTextObject(text="Good", emoji=None), + value="positive_feedback", + accessibility_label="Mark this response as good", + ), + negative_button=FeedbackButtonObject( + text=PlainTextObject(text="Bad", emoji=None), + value="negative_feedback", + accessibility_label="Mark this response as bad", + ), + ), + ] + ) + return block diff --git a/block-kit/src/block_elements/icon_button.py b/block-kit/src/block_elements/icon_button.py new file mode 100644 index 0000000..96b43f5 --- /dev/null +++ b/block-kit/src/block_elements/icon_button.py @@ -0,0 +1,23 @@ +from slack_sdk.models.blocks import ContextActionsBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import IconButtonElement + + +def example01() -> ContextActionsBlock: + """ + An icon button to perform actions. + https://docs.slack.dev/reference/block-kit/block-elements/icon-button-element/ + + A context actions block holding an icon button element. + """ + block = ContextActionsBlock( + elements=[ + IconButtonElement( + icon="trash", + text=PlainTextObject(text="Delete", emoji=None), + action_id="delete_button", + value="delete_item", + ), + ] + ) + return block diff --git a/block-kit/src/block_elements/link.py b/block-kit/src/block_elements/link.py new file mode 100644 index 0000000..c9b779b --- /dev/null +++ b/block-kit/src/block_elements/link.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a hyperlink. + https://docs.slack.dev/reference/block-kit/block-elements/link-element/ + + A rich text block whose section holds a link element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Link(url="https://docs.slack.dev"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/rich_text_input.py b/block-kit/src/block_elements/rich_text_input.py new file mode 100644 index 0000000..2dc0d29 --- /dev/null +++ b/block-kit/src/block_elements/rich_text_input.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import InputBlock +from slack_sdk.models.blocks.basic_components import PlainTextObject +from slack_sdk.models.blocks.block_elements import RichTextInputElement + + +def example01() -> InputBlock: + """ + Allows users to enter formatted text in a WYSIWYG composer, offering the same messaging writing experience as in Slack. + https://docs.slack.dev/reference/block-kit/block-elements/rich-text-input-element/ + + An input block containing a rich text input element. + """ + block = InputBlock( + element=RichTextInputElement( + action_id="rich_text_input-action", + dispatch_action_config={ + "trigger_actions_on": ["on_character_entered"], + }, + focus_on_load=True, + placeholder=PlainTextObject(text="Enter text", emoji=None), + ), + label=PlainTextObject(text="Label", emoji=True), + ) + return block diff --git a/block-kit/src/block_elements/rich_text_list.py b/block-kit/src/block_elements/rich_text_list.py new file mode 100644 index 0000000..7d659fe --- /dev/null +++ b/block-kit/src/block_elements/rich_text_list.py @@ -0,0 +1,95 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextListElement, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a list of rich text items. + https://docs.slack.dev/reference/block-kit/block-elements/rich-text-list-element/ + + A rich text block with a bordered bullet list. + """ + block = RichTextBlock( + block_id="block1", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text( + text="My favorite Slack features (in no particular order):" + ), + ] + ), + RichTextListElement( + style="bullet", + indent=0, + border=1, + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Huddles")] + ), + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Canvas")] + ), + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Developing with Block Kit") + ] + ), + ], + ), + ], + ) + return block + + +def example02() -> RichTextBlock: + """ + A rich text block with a nested bullet list, created by indenting the + middle list into a sub-list. + """ + block = RichTextBlock( + block_id="block1", + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Breakfast foods I enjoy:")] + ), + RichTextListElement( + style="bullet", + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Hashbrowns")] + ), + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Eggs")] + ), + ], + ), + RichTextListElement( + style="bullet", + indent=1, + elements=[ + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Scrambled")] + ), + RichTextSectionElement( + elements=[RichTextElementParts.Text(text="Over easy")] + ), + ], + ), + RichTextListElement( + style="bullet", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Pancakes, extra syrup") + ] + ), + ], + ), + ], + ) + return block diff --git a/block-kit/src/block_elements/rich_text_preformatted.py b/block-kit/src/block_elements/rich_text_preformatted.py new file mode 100644 index 0000000..73d1811 --- /dev/null +++ b/block-kit/src/block_elements/rich_text_preformatted.py @@ -0,0 +1,27 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextPreformattedElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a preformatted rich text element. + https://docs.slack.dev/reference/block-kit/block-elements/rich-text-preformatted-element/ + + A rich text block with a borderless preformatted code snippet. + """ + block = RichTextBlock( + elements=[ + RichTextPreformattedElement( + border=0, + elements=[ + RichTextElementParts.Text( + text='{\n "object": {\n "description": "this is an example of a json object"\n }\n}' + ), + ], + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/rich_text_quote.py b/block-kit/src/block_elements/rich_text_quote.py new file mode 100644 index 0000000..8d610b4 --- /dev/null +++ b/block-kit/src/block_elements/rich_text_quote.py @@ -0,0 +1,33 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextQuoteElement, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays a rich text quote block. + https://docs.slack.dev/reference/block-kit/block-elements/rich-text-quote-element/ + + A rich text block with a quote followed by a section. + """ + block = RichTextBlock( + block_id="Vrzsu", + elements=[ + RichTextQuoteElement( + elements=[ + RichTextElementParts.Text( + text="What we need is good examples in our documentation." + ), + ] + ), + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Yes - I completely agree, Luke!"), + ] + ), + ], + ) + return block diff --git a/block-kit/src/block_elements/rich_text_section.py b/block-kit/src/block_elements/rich_text_section.py new file mode 100644 index 0000000..fa6d2b9 --- /dev/null +++ b/block-kit/src/block_elements/rich_text_section.py @@ -0,0 +1,67 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> list[RichTextBlock]: + """ + A section element that holds rich text elements. + https://docs.slack.dev/reference/block-kit/block-elements/rich-text-section-element/ + + Four rich text blocks showing basic, bold, italic, and strikethrough text. + """ + blocks = [ + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text( + text="Hello there, I am a basic rich text block!" + ), + ] + ) + ] + ), + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am a bold rich text block!", + style={"bold": True}, + ), + ] + ) + ] + ), + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am an italic rich text block!", + style={"italic": True}, + ), + ] + ) + ] + ), + RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am a strikethrough rich text block!", + style={"strike": True}, + ), + ] + ) + ] + ), + ] + return blocks diff --git a/block-kit/src/block_elements/team.py b/block-kit/src/block_elements/team.py new file mode 100644 index 0000000..396806d --- /dev/null +++ b/block-kit/src/block_elements/team.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Renders as a mention of a workspace or team. + https://docs.slack.dev/reference/block-kit/block-elements/team-element/ + + A rich text block whose section holds a team mention element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Team(team_id="T123ABC456"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/text.py b/block-kit/src/block_elements/text.py new file mode 100644 index 0000000..a457b89 --- /dev/null +++ b/block-kit/src/block_elements/text.py @@ -0,0 +1,28 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Displays text, optionally with styling. + https://docs.slack.dev/reference/block-kit/block-elements/text-element/ + + A rich text block whose section holds plain and bold-styled text elements. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Hello there, "), + RichTextElementParts.Text( + text="I am a bold rich text block!", + style={"bold": True}, + ), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/url_source.py b/block-kit/src/block_elements/url_source.py new file mode 100644 index 0000000..a25281e --- /dev/null +++ b/block-kit/src/block_elements/url_source.py @@ -0,0 +1,38 @@ +from slack_sdk.models.blocks import TaskCardBlock +from slack_sdk.models.blocks.block_elements import UrlSourceElement + + +def example01() -> UrlSourceElement: + """ + Displays a URL source with optional icon. + https://docs.slack.dev/reference/block-kit/block-elements/url-source-element/ + + A standalone URL source element. + """ + element = UrlSourceElement( + url="https://docs.slack.dev/", + text="Slack API docs", + ) + return element + + +def example02() -> TaskCardBlock: + """ + A task card block whose sources are URL source elements. + """ + block = TaskCardBlock( + task_id="task_1", + title="Scientific findings", + status="complete", + sources=[ + UrlSourceElement( + url="https://docs.example.com/", + text="Tracy's delightful docs", + ), + UrlSourceElement( + url="https://research.example.com/", + text="Haley's resourceful research", + ), + ], + ) + return block diff --git a/block-kit/src/block_elements/user.py b/block-kit/src/block_elements/user.py new file mode 100644 index 0000000..9dabe50 --- /dev/null +++ b/block-kit/src/block_elements/user.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Renders as a mention of a user. + https://docs.slack.dev/reference/block-kit/block-elements/user-element/ + + A rich text block whose section holds a user mention element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.User(user_id="U123ABC456"), + ] + ) + ] + ) + return block diff --git a/block-kit/src/block_elements/usergroup.py b/block-kit/src/block_elements/usergroup.py new file mode 100644 index 0000000..71b93d0 --- /dev/null +++ b/block-kit/src/block_elements/usergroup.py @@ -0,0 +1,24 @@ +from slack_sdk.models.blocks import RichTextBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> RichTextBlock: + """ + Renders as a mention of a user group. + https://docs.slack.dev/reference/block-kit/block-elements/usergroup-element/ + + A rich text block whose section holds a user group mention element. + """ + block = RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.UserGroup(usergroup_id="G123ABC456"), + ] + ) + ] + ) + return block diff --git a/block-kit/tests/block_elements/test_broadcast.py b/block-kit/tests/block_elements/test_broadcast.py new file mode 100644 index 0000000..27dfff9 --- /dev/null +++ b/block-kit/tests/block_elements/test_broadcast.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import broadcast + + +def test_example01(): + block = broadcast.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "broadcast", + "range": "everyone", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_button.py b/block-kit/tests/block_elements/test_button.py index f484188..3180252 100644 --- a/block-kit/tests/block_elements/test_button.py +++ b/block-kit/tests/block_elements/test_button.py @@ -8,7 +8,10 @@ def test_example01(): actual = element.to_dict() expected = { "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, + "text": { + "type": "plain_text", + "text": "Click Me", + }, "value": "click_me_123", "action_id": "button", } @@ -20,7 +23,10 @@ def test_example02(): actual = element.to_dict() expected = { "type": "button", - "text": {"type": "plain_text", "text": "Save"}, + "text": { + "type": "plain_text", + "text": "Save", + }, "style": "primary", "value": "click_me_123", "action_id": "button", @@ -33,7 +39,10 @@ def test_example03(): actual = element.to_dict() expected = { "type": "button", - "text": {"type": "plain_text", "text": "Link Button"}, + "text": { + "type": "plain_text", + "text": "Link Button", + }, "url": "https://docs.slack.dev/block-kit", } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) @@ -51,7 +60,10 @@ def test_example04(): }, "accessory": { "type": "button", - "text": {"type": "plain_text", "text": "Click Me"}, + "text": { + "type": "plain_text", + "text": "Click Me", + }, "value": "click_me_123", "action_id": "button", }, @@ -62,13 +74,19 @@ def test_example04(): "elements": [ { "type": "button", - "text": {"type": "plain_text", "text": "Primary Button"}, + "text": { + "type": "plain_text", + "text": "Primary Button", + }, "style": "primary", "value": "click_me_456", }, { "type": "button", - "text": {"type": "plain_text", "text": "Link Button"}, + "text": { + "type": "plain_text", + "text": "Link Button", + }, "url": "https://api.slack.com/block-kit", }, ], diff --git a/block-kit/tests/block_elements/test_channel.py b/block-kit/tests/block_elements/test_channel.py new file mode 100644 index 0000000..fdb8f56 --- /dev/null +++ b/block-kit/tests/block_elements/test_channel.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import channel + + +def test_example01(): + block = channel.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "channel", + "channel_id": "C123ABC456", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_checkboxes.py b/block-kit/tests/block_elements/test_checkboxes.py index db1f8b7..2956521 100644 --- a/block-kit/tests/block_elements/test_checkboxes.py +++ b/block-kit/tests/block_elements/test_checkboxes.py @@ -8,18 +8,36 @@ def test_example01(): actual = block.to_dict() expected = { "type": "section", - "text": {"type": "plain_text", "text": "Check out these charming checkboxes"}, + "text": { + "type": "plain_text", + "text": "Check out these charming checkboxes", + }, "accessory": { "type": "checkboxes", "action_id": "this_is_an_action_id", "initial_options": [ - {"value": "A1", "text": {"type": "plain_text", "text": "Checkbox 1"}}, + { + "value": "A1", + "text": { + "type": "plain_text", + "text": "Checkbox 1", + }, + }, ], "options": [ - {"value": "A1", "text": {"type": "plain_text", "text": "Checkbox 1"}}, + { + "value": "A1", + "text": { + "type": "plain_text", + "text": "Checkbox 1", + }, + }, { "value": "A2", - "text": {"type": "plain_text", "text": "Checkbox 2"}, + "text": { + "type": "plain_text", + "text": "Checkbox 2", + }, "description": { "type": "mrkdwn", "text": "*A description of option two*", diff --git a/block-kit/tests/block_elements/test_color.py b/block-kit/tests/block_elements/test_color.py new file mode 100644 index 0000000..0d6f544 --- /dev/null +++ b/block-kit/tests/block_elements/test_color.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import color + + +def test_example01(): + block = color.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "color", + "value": "#F405B3", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_date.py b/block-kit/tests/block_elements/test_date.py new file mode 100644 index 0000000..53f5c2a --- /dev/null +++ b/block-kit/tests/block_elements/test_date.py @@ -0,0 +1,25 @@ +import json + +from src.block_elements import date + + +def test_example01(): + block = date.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "date", + "timestamp": 1720710212, + "format": "{date_num} at {time}", + "fallback": "timey", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_date_picker.py b/block-kit/tests/block_elements/test_date_picker.py index 9fb73ab..16db365 100644 --- a/block-kit/tests/block_elements/test_date_picker.py +++ b/block-kit/tests/block_elements/test_date_picker.py @@ -9,12 +9,18 @@ def test_example01(): expected = { "type": "section", "block_id": "section1234", - "text": {"type": "mrkdwn", "text": "Pick a date for the deadline."}, + "text": { + "type": "mrkdwn", + "text": "Pick a date for the deadline.", + }, "accessory": { "type": "datepicker", "action_id": "datepicker123", "initial_date": "1990-04-28", - "placeholder": {"type": "plain_text", "text": "Select a date"}, + "placeholder": { + "type": "plain_text", + "text": "Select a date", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_datetime_picker.py b/block-kit/tests/block_elements/test_datetime_picker.py index 4a3ce3b..60098ce 100644 --- a/block-kit/tests/block_elements/test_datetime_picker.py +++ b/block-kit/tests/block_elements/test_datetime_picker.py @@ -8,8 +8,19 @@ def test_example01(): actual = block.to_dict() expected = { "type": "input", - "element": {"type": "datetimepicker", "action_id": "datetimepicker-action"}, - "hint": {"type": "plain_text", "text": "This is some hint text", "emoji": True}, - "label": {"type": "plain_text", "text": "Start date", "emoji": True}, + "element": { + "type": "datetimepicker", + "action_id": "datetimepicker-action", + }, + "hint": { + "type": "plain_text", + "text": "This is some hint text", + "emoji": True, + }, + "label": { + "type": "plain_text", + "text": "Start date", + "emoji": True, + }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_email_input.py b/block-kit/tests/block_elements/test_email_input.py index c9ececc..1758b25 100644 --- a/block-kit/tests/block_elements/test_email_input.py +++ b/block-kit/tests/block_elements/test_email_input.py @@ -9,11 +9,17 @@ def test_example01(): expected = { "type": "input", "block_id": "input123", - "label": {"type": "plain_text", "text": "Email Address"}, + "label": { + "type": "plain_text", + "text": "Email Address", + }, "element": { "type": "email_text_input", "action_id": "email_text_input-action", - "placeholder": {"type": "plain_text", "text": "Enter an email"}, + "placeholder": { + "type": "plain_text", + "text": "Enter an email", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_emoji.py b/block-kit/tests/block_elements/test_emoji.py new file mode 100644 index 0000000..dfe7851 --- /dev/null +++ b/block-kit/tests/block_elements/test_emoji.py @@ -0,0 +1,39 @@ +import json + +from src.block_elements import emoji + + +def test_example01(): + block = emoji.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "emoji", + "name": "basketball", + }, + { + "type": "text", + "text": " ", + }, + { + "type": "emoji", + "name": "snowboarder", + }, + { + "type": "text", + "text": " ", + }, + { + "type": "emoji", + "name": "checkered_flag", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_feedback_buttons.py b/block-kit/tests/block_elements/test_feedback_buttons.py new file mode 100644 index 0000000..9fc5c8f --- /dev/null +++ b/block-kit/tests/block_elements/test_feedback_buttons.py @@ -0,0 +1,34 @@ +import json + +from src.block_elements import feedback_buttons + + +def test_example01(): + block = feedback_buttons.example01() + actual = block.to_dict() + expected = { + "type": "context_actions", + "elements": [ + { + "type": "feedback_buttons", + "action_id": "feedback_buttons_1", + "positive_button": { + "text": { + "type": "plain_text", + "text": "Good", + }, + "value": "positive_feedback", + "accessibility_label": "Mark this response as good", + }, + "negative_button": { + "text": { + "type": "plain_text", + "text": "Bad", + }, + "value": "negative_feedback", + "accessibility_label": "Mark this response as bad", + }, + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_file_input.py b/block-kit/tests/block_elements/test_file_input.py index 97cc1d4..8e53c5c 100644 --- a/block-kit/tests/block_elements/test_file_input.py +++ b/block-kit/tests/block_elements/test_file_input.py @@ -9,11 +9,17 @@ def test_example01(): expected = { "type": "input", "block_id": "input_block_id", - "label": {"type": "plain_text", "text": "Upload Files"}, + "label": { + "type": "plain_text", + "text": "Upload Files", + }, "element": { "type": "file_input", "action_id": "file_input_action_id_1", - "filetypes": ["jpg", "png"], + "filetypes": [ + "jpg", + "png", + ], "max_files": 5, }, } diff --git a/block-kit/tests/block_elements/test_icon_button.py b/block-kit/tests/block_elements/test_icon_button.py new file mode 100644 index 0000000..1f5a341 --- /dev/null +++ b/block-kit/tests/block_elements/test_icon_button.py @@ -0,0 +1,24 @@ +import json + +from src.block_elements import icon_button + + +def test_example01(): + block = icon_button.example01() + actual = block.to_dict() + expected = { + "type": "context_actions", + "elements": [ + { + "type": "icon_button", + "icon": "trash", + "text": { + "type": "plain_text", + "text": "Delete", + }, + "action_id": "delete_button", + "value": "delete_item", + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_image.py b/block-kit/tests/block_elements/test_image.py index 5d5693e..19a34fc 100644 --- a/block-kit/tests/block_elements/test_image.py +++ b/block-kit/tests/block_elements/test_image.py @@ -35,7 +35,7 @@ def test_example02(): "accessory": { "type": "image", "slack_file": { - "url": "https://files.slack.com/files-pri/T0123456-F0123456/xyz.png" + "url": "https://files.slack.com/files-pri/T0123456-F0123456/xyz.png", }, "alt_text": "Slack file object.", }, @@ -55,7 +55,9 @@ def test_example03(): }, "accessory": { "type": "image", - "slack_file": {"id": "F01234567"}, + "slack_file": { + "id": "F01234567", + }, "alt_text": "Slack file object.", }, } diff --git a/block-kit/tests/block_elements/test_link.py b/block-kit/tests/block_elements/test_link.py new file mode 100644 index 0000000..3667c90 --- /dev/null +++ b/block-kit/tests/block_elements/test_link.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import link + + +def test_example01(): + block = link.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "link", + "url": "https://docs.slack.dev", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_multi_select_menu.py b/block-kit/tests/block_elements/test_multi_select_menu.py index 83670d9..96a2f8c 100644 --- a/block-kit/tests/block_elements/test_multi_select_menu.py +++ b/block-kit/tests/block_elements/test_multi_select_menu.py @@ -9,22 +9,37 @@ def test_example01(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick items from the list"}, + "text": { + "type": "mrkdwn", + "text": "Pick items from the list", + }, "accessory": { "type": "multi_static_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select items"}, + "placeholder": { + "type": "plain_text", + "text": "Select items", + }, "options": [ { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-0", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-1", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-2", }, ], @@ -39,11 +54,17 @@ def test_example02(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick items from the list"}, + "text": { + "type": "mrkdwn", + "text": "Pick items from the list", + }, "accessory": { "type": "multi_external_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select items"}, + "placeholder": { + "type": "plain_text", + "text": "Select items", + }, "min_query_length": 3, }, } @@ -56,11 +77,17 @@ def test_example03(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick users from the list"}, + "text": { + "type": "mrkdwn", + "text": "Pick users from the list", + }, "accessory": { "type": "multi_users_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select users"}, + "placeholder": { + "type": "plain_text", + "text": "Select users", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) @@ -72,11 +99,17 @@ def test_example04(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick conversations from the list"}, + "text": { + "type": "mrkdwn", + "text": "Pick conversations from the list", + }, "accessory": { "type": "multi_conversations_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select conversations"}, + "placeholder": { + "type": "plain_text", + "text": "Select conversations", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) @@ -88,11 +121,17 @@ def test_example05(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick channels from the list"}, + "text": { + "type": "mrkdwn", + "text": "Pick channels from the list", + }, "accessory": { "type": "multi_channels_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select channels"}, + "placeholder": { + "type": "plain_text", + "text": "Select channels", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_number_input.py b/block-kit/tests/block_elements/test_number_input.py index c20ded6..65dd15e 100644 --- a/block-kit/tests/block_elements/test_number_input.py +++ b/block-kit/tests/block_elements/test_number_input.py @@ -13,6 +13,10 @@ def test_example01(): "is_decimal_allowed": False, "action_id": "number_input-action", }, - "label": {"type": "plain_text", "text": "Label", "emoji": True}, + "label": { + "type": "plain_text", + "text": "Label", + "emoji": True, + }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_overflow_menu.py b/block-kit/tests/block_elements/test_overflow_menu.py index 2aed0b5..a87dbc5 100644 --- a/block-kit/tests/block_elements/test_overflow_menu.py +++ b/block-kit/tests/block_elements/test_overflow_menu.py @@ -18,23 +18,38 @@ def test_example01(): "action_id": "overflow", "options": [ { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-0", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-1", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-2", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-3", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-4", }, ], diff --git a/block-kit/tests/block_elements/test_plain_text_input.py b/block-kit/tests/block_elements/test_plain_text_input.py index d7b110d..74f616c 100644 --- a/block-kit/tests/block_elements/test_plain_text_input.py +++ b/block-kit/tests/block_elements/test_plain_text_input.py @@ -12,6 +12,10 @@ def test_example01(): "type": "plain_text_input", "action_id": "plain_text_input-action", }, - "label": {"type": "plain_text", "text": "Label", "emoji": True}, + "label": { + "type": "plain_text", + "text": "Label", + "emoji": True, + }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_radio_buttons.py b/block-kit/tests/block_elements/test_radio_buttons.py index 46f5c11..1c41352 100644 --- a/block-kit/tests/block_elements/test_radio_buttons.py +++ b/block-kit/tests/block_elements/test_radio_buttons.py @@ -8,17 +8,35 @@ def test_example01(): actual = block.to_dict() expected = { "type": "section", - "text": {"type": "plain_text", "text": "Check out these rad radio buttons"}, + "text": { + "type": "plain_text", + "text": "Check out these rad radio buttons", + }, "accessory": { "type": "radio_buttons", "action_id": "this_is_an_action_id", "initial_option": { "value": "A1", - "text": {"type": "plain_text", "text": "Radio 1"}, + "text": { + "type": "plain_text", + "text": "Radio 1", + }, }, "options": [ - {"value": "A1", "text": {"type": "plain_text", "text": "Radio 1"}}, - {"value": "A2", "text": {"type": "plain_text", "text": "Radio 2"}}, + { + "value": "A1", + "text": { + "type": "plain_text", + "text": "Radio 1", + }, + }, + { + "value": "A2", + "text": { + "type": "plain_text", + "text": "Radio 2", + }, + }, ], }, } diff --git a/block-kit/tests/block_elements/test_rich_text_input.py b/block-kit/tests/block_elements/test_rich_text_input.py new file mode 100644 index 0000000..d0a85a8 --- /dev/null +++ b/block-kit/tests/block_elements/test_rich_text_input.py @@ -0,0 +1,31 @@ +import json + +from src.block_elements import rich_text_input + + +def test_example01(): + block = rich_text_input.example01() + actual = block.to_dict() + expected = { + "type": "input", + "label": { + "type": "plain_text", + "text": "Label", + "emoji": True, + }, + "element": { + "type": "rich_text_input", + "action_id": "rich_text_input-action", + "dispatch_action_config": { + "trigger_actions_on": [ + "on_character_entered", + ], + }, + "focus_on_load": True, + "placeholder": { + "type": "plain_text", + "text": "Enter text", + }, + }, + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_rich_text_list.py b/block-kit/tests/block_elements/test_rich_text_list.py new file mode 100644 index 0000000..cf57906 --- /dev/null +++ b/block-kit/tests/block_elements/test_rich_text_list.py @@ -0,0 +1,144 @@ +import json + +from src.block_elements import rich_text_list + + +def test_example01(): + block = rich_text_list.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "block_id": "block1", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "My favorite Slack features (in no particular order):", + }, + ], + }, + { + "type": "rich_text_list", + "style": "bullet", + "indent": 0, + "border": 1, + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Huddles", + }, + ], + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Canvas", + }, + ], + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Developing with Block Kit", + }, + ], + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = rich_text_list.example02() + actual = block.to_dict() + expected = { + "type": "rich_text", + "block_id": "block1", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Breakfast foods I enjoy:", + }, + ], + }, + { + "type": "rich_text_list", + "style": "bullet", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hashbrowns", + }, + ], + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Eggs", + }, + ], + }, + ], + }, + { + "type": "rich_text_list", + "style": "bullet", + "indent": 1, + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Scrambled", + }, + ], + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Over easy", + }, + ], + }, + ], + }, + { + "type": "rich_text_list", + "style": "bullet", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Pancakes, extra syrup", + }, + ], + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_rich_text_preformatted.py b/block-kit/tests/block_elements/test_rich_text_preformatted.py new file mode 100644 index 0000000..4da8e24 --- /dev/null +++ b/block-kit/tests/block_elements/test_rich_text_preformatted.py @@ -0,0 +1,24 @@ +import json + +from src.block_elements import rich_text_preformatted + + +def test_example01(): + block = rich_text_preformatted.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_preformatted", + "border": 0, + "elements": [ + { + "type": "text", + "text": '{\n "object": {\n "description": "this is an example of a json object"\n }\n}', + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_rich_text_quote.py b/block-kit/tests/block_elements/test_rich_text_quote.py new file mode 100644 index 0000000..1f40444 --- /dev/null +++ b/block-kit/tests/block_elements/test_rich_text_quote.py @@ -0,0 +1,33 @@ +import json + +from src.block_elements import rich_text_quote + + +def test_example01(): + block = rich_text_quote.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "block_id": "Vrzsu", + "elements": [ + { + "type": "rich_text_quote", + "elements": [ + { + "type": "text", + "text": "What we need is good examples in our documentation.", + }, + ], + }, + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Yes - I completely agree, Luke!", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_rich_text_section.py b/block-kit/tests/block_elements/test_rich_text_section.py new file mode 100644 index 0000000..a34ab44 --- /dev/null +++ b/block-kit/tests/block_elements/test_rich_text_section.py @@ -0,0 +1,88 @@ +import json + +from src.block_elements import rich_text_section + + +def test_example01(): + blocks = rich_text_section.example01() + actual = [block.to_dict() for block in blocks] + expected = [ + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, I am a basic rich text block!", + }, + ], + }, + ], + }, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, ", + }, + { + "type": "text", + "text": "I am a bold rich text block!", + "style": { + "bold": True, + }, + }, + ], + }, + ], + }, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, ", + }, + { + "type": "text", + "text": "I am an italic rich text block!", + "style": { + "italic": True, + }, + }, + ], + }, + ], + }, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, ", + }, + { + "type": "text", + "text": "I am a strikethrough rich text block!", + "style": { + "strike": True, + }, + }, + ], + }, + ], + }, + ] + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_select_menu.py b/block-kit/tests/block_elements/test_select_menu.py index 19c0128..aa98165 100644 --- a/block-kit/tests/block_elements/test_select_menu.py +++ b/block-kit/tests/block_elements/test_select_menu.py @@ -9,22 +9,37 @@ def test_example01(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick an item from the dropdown list"}, + "text": { + "type": "mrkdwn", + "text": "Pick an item from the dropdown list", + }, "accessory": { "type": "static_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select an item"}, + "placeholder": { + "type": "plain_text", + "text": "Select an item", + }, "options": [ { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-0", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-1", }, { - "text": {"type": "plain_text", "text": "*this is plain_text text*"}, + "text": { + "type": "plain_text", + "text": "*this is plain_text text*", + }, "value": "value-2", }, ], @@ -39,11 +54,17 @@ def test_example02(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick an item from the dropdown list"}, + "text": { + "type": "mrkdwn", + "text": "Pick an item from the dropdown list", + }, "accessory": { "type": "external_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select an item"}, + "placeholder": { + "type": "plain_text", + "text": "Select an item", + }, "min_query_length": 3, }, } @@ -56,11 +77,17 @@ def test_example03(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick a user from the dropdown list"}, + "text": { + "type": "mrkdwn", + "text": "Pick a user from the dropdown list", + }, "accessory": { "type": "users_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select an item"}, + "placeholder": { + "type": "plain_text", + "text": "Select an item", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) @@ -79,7 +106,10 @@ def test_example04(): "accessory": { "type": "conversations_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select an item"}, + "placeholder": { + "type": "plain_text", + "text": "Select an item", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) @@ -91,11 +121,17 @@ def test_example05(): expected = { "type": "section", "block_id": "section678", - "text": {"type": "mrkdwn", "text": "Pick a channel from the dropdown list"}, + "text": { + "type": "mrkdwn", + "text": "Pick a channel from the dropdown list", + }, "accessory": { "type": "channels_select", "action_id": "text1234", - "placeholder": {"type": "plain_text", "text": "Select an item"}, + "placeholder": { + "type": "plain_text", + "text": "Select an item", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_team.py b/block-kit/tests/block_elements/test_team.py new file mode 100644 index 0000000..b7fe0ba --- /dev/null +++ b/block-kit/tests/block_elements/test_team.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import team + + +def test_example01(): + block = team.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "team", + "team_id": "T123ABC456", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_text.py b/block-kit/tests/block_elements/test_text.py new file mode 100644 index 0000000..cc0849f --- /dev/null +++ b/block-kit/tests/block_elements/test_text.py @@ -0,0 +1,30 @@ +import json + +from src.block_elements import text + + +def test_example01(): + block = text.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Hello there, ", + }, + { + "type": "text", + "text": "I am a bold rich text block!", + "style": { + "bold": True, + }, + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_time_picker.py b/block-kit/tests/block_elements/test_time_picker.py index 81bf035..256c925 100644 --- a/block-kit/tests/block_elements/test_time_picker.py +++ b/block-kit/tests/block_elements/test_time_picker.py @@ -9,13 +9,19 @@ def test_example01(): expected = { "type": "section", "block_id": "section1234", - "text": {"type": "mrkdwn", "text": "Pick a date for the deadline."}, + "text": { + "type": "mrkdwn", + "text": "Pick a date for the deadline.", + }, "accessory": { "type": "timepicker", "timezone": "America/Los_Angeles", "action_id": "timepicker123", "initial_time": "11:40", - "placeholder": {"type": "plain_text", "text": "Select a time"}, + "placeholder": { + "type": "plain_text", + "text": "Select a time", + }, }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_url_input.py b/block-kit/tests/block_elements/test_url_input.py index 7b6e858..bb0c737 100644 --- a/block-kit/tests/block_elements/test_url_input.py +++ b/block-kit/tests/block_elements/test_url_input.py @@ -8,7 +8,14 @@ def test_example01(): actual = block.to_dict() expected = { "type": "input", - "element": {"type": "url_text_input", "action_id": "url_text_input-action"}, - "label": {"type": "plain_text", "text": "Label", "emoji": True}, + "element": { + "type": "url_text_input", + "action_id": "url_text_input-action", + }, + "label": { + "type": "plain_text", + "text": "Label", + "emoji": True, + }, } assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_url_source.py b/block-kit/tests/block_elements/test_url_source.py new file mode 100644 index 0000000..41c286a --- /dev/null +++ b/block-kit/tests/block_elements/test_url_source.py @@ -0,0 +1,38 @@ +import json + +from src.block_elements import url_source + + +def test_example01(): + element = url_source.example01() + actual = element.to_dict() + expected = { + "type": "url", + "url": "https://docs.slack.dev/", + "text": "Slack API docs", + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) + + +def test_example02(): + block = url_source.example02() + actual = block.to_dict() + expected = { + "type": "task_card", + "task_id": "task_1", + "title": "Scientific findings", + "status": "complete", + "sources": [ + { + "type": "url", + "url": "https://docs.example.com/", + "text": "Tracy's delightful docs", + }, + { + "type": "url", + "url": "https://research.example.com/", + "text": "Haley's resourceful research", + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_user.py b/block-kit/tests/block_elements/test_user.py new file mode 100644 index 0000000..2c72e00 --- /dev/null +++ b/block-kit/tests/block_elements/test_user.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import user + + +def test_example01(): + block = user.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "user", + "user_id": "U123ABC456", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_usergroup.py b/block-kit/tests/block_elements/test_usergroup.py new file mode 100644 index 0000000..94d629d --- /dev/null +++ b/block-kit/tests/block_elements/test_usergroup.py @@ -0,0 +1,23 @@ +import json + +from src.block_elements import usergroup + + +def test_example01(): + block = usergroup.example01() + actual = block.to_dict() + expected = { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "usergroup", + "usergroup_id": "G123ABC456", + }, + ], + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/block_elements/test_workflow_button.py b/block-kit/tests/block_elements/test_workflow_button.py index 58f3be2..184a365 100644 --- a/block-kit/tests/block_elements/test_workflow_button.py +++ b/block-kit/tests/block_elements/test_workflow_button.py @@ -14,7 +14,10 @@ def test_example01(): }, "accessory": { "type": "workflow_button", - "text": {"type": "plain_text", "text": "Run Workflow"}, + "text": { + "type": "plain_text", + "text": "Run Workflow", + }, "action_id": "workflowbutton123", "workflow": { "trigger": { @@ -29,7 +32,7 @@ def test_example01(): "value": "Value for input param B", }, ], - } + }, }, }, }