diff --git a/block-kit/README.md b/block-kit/README.md index 5f7a51c..ee77ff0 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -9,14 +9,16 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes ### Blocks - **[Actions](https://docs.slack.dev/reference/block-kit/blocks/actions-block)**: Holds multiple interactive elements. [Implementation](./src/blocks/actions.py). -- **[Context actions](https://docs.slack.dev/reference/block-kit/blocks/context-actions-block)**: Displays actions as contextual info, which can include both feedback buttons and icon buttons. [Implementation](./src/blocks/context_actions.py). - **[Context](https://docs.slack.dev/reference/block-kit/blocks/context-block)**: Provides contextual info, which can include both images and text. [Implementation](./src/blocks/context.py). +- **[Context actions](https://docs.slack.dev/reference/block-kit/blocks/context-actions-block)**: Displays actions as contextual info, which can include both feedback buttons and icon buttons. [Implementation](./src/blocks/context_actions.py). - **[Divider](https://docs.slack.dev/reference/block-kit/blocks/divider-block)**: Visually separates pieces of info inside of a message. [Implementation](./src/blocks/divider.py). - **[File](https://docs.slack.dev/reference/block-kit/blocks/file-block)**: Displays info about remote files. [Implementation](./src/blocks/file.py). - **[Header](https://docs.slack.dev/reference/block-kit/blocks/header-block)**: Displays a larger-sized text. [Implementation](./src/blocks/header.py). - **[Image](https://docs.slack.dev/reference/block-kit/blocks/image-block)**: Displays an image. [Implementation](./src/blocks/image.py). - **[Input](https://docs.slack.dev/reference/block-kit/blocks/input-block)**: Collects information from users via elements. [Implementation](./src/blocks/input.py). - **[Markdown](https://docs.slack.dev/reference/block-kit/blocks/markdown-block)**: Displays formatted markdown. [Implementation](./src/blocks/markdown.py). +- **[Plan](https://docs.slack.dev/reference/block-kit/blocks/plan-block)**: Displays a collection of related tasks. [Implementation](./src/blocks/plan.py). - **[Rich text](https://docs.slack.dev/reference/block-kit/blocks/rich-text-block)**: Displays formatted, structured representation of text. [Implementation](./src/blocks/rich_text.py). - **[Section](https://docs.slack.dev/reference/block-kit/blocks/section-block)**: Displays text, possibly alongside elements. [Implementation](./src/blocks/section.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). diff --git a/block-kit/src/blocks/plan.py b/block-kit/src/blocks/plan.py new file mode 100644 index 0000000..8872d9d --- /dev/null +++ b/block-kit/src/blocks/plan.py @@ -0,0 +1,67 @@ +from slack_sdk.models.blocks import PlanBlock, RichTextBlock, TaskCardBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, +) + + +def example01() -> PlanBlock: + """ + Displays a collection of related tasks. + https://docs.slack.dev/reference/block-kit/blocks/plan-block/ + + A plan block with multiple task cards in various states. + """ + block = PlanBlock( + title="Thinking completed", + tasks=[ + TaskCardBlock( + task_id="call_001", + title="Fetched user profile information", + status="in_progress", + details=RichTextBlock( + block_id="viMWO", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Searched database...") + ] + ) + ], + ), + output=RichTextBlock( + block_id="viMWO", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text(text="Profile data loaded") + ] + ) + ], + ), + ), + TaskCardBlock( + task_id="call_002", + title="Checked user permissions", + status="pending", + ), + TaskCardBlock( + task_id="call_003", + title="Generated comprehensive user report", + status="complete", + output=RichTextBlock( + block_id="crsk", + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text( + text="15 data points compiled" + ) + ] + ) + ], + ), + ), + ], + ) + return block diff --git a/block-kit/src/blocks/task_card.py b/block-kit/src/blocks/task_card.py new file mode 100644 index 0000000..9e2a0fb --- /dev/null +++ b/block-kit/src/blocks/task_card.py @@ -0,0 +1,42 @@ +from slack_sdk.models.blocks import RichTextBlock, TaskCardBlock +from slack_sdk.models.blocks.block_elements import ( + RichTextElementParts, + RichTextSectionElement, + UrlSourceElement, +) + + +def example01() -> TaskCardBlock: + """ + Displays a single task, representing a single action. + https://docs.slack.dev/reference/block-kit/blocks/task-card-block/ + + A task card with output and sources. + """ + block = TaskCardBlock( + task_id="task_1", + title="Fetching weather data", + status="pending", + output=RichTextBlock( + elements=[ + RichTextSectionElement( + elements=[ + RichTextElementParts.Text( + text="Found weather data for Chicago from 2 sources" + ) + ] + ) + ] + ), + sources=[ + UrlSourceElement( + url="https://weather.com/", + text="weather.com", + ), + UrlSourceElement( + url="https://www.accuweather.com/", + text="accuweather.com", + ), + ], + ) + return block diff --git a/block-kit/tests/blocks/test_plan.py b/block-kit/tests/blocks/test_plan.py new file mode 100644 index 0000000..e806350 --- /dev/null +++ b/block-kit/tests/blocks/test_plan.py @@ -0,0 +1,78 @@ +import json + +from src.blocks import plan + + +def test_example01(): + block = plan.example01() + actual = block.to_dict() + expected = { + "type": "plan", + "title": "Thinking completed", + "tasks": [ + { + "type": "task_card", + "task_id": "call_001", + "title": "Fetched user profile information", + "status": "in_progress", + "details": { + "type": "rich_text", + "block_id": "viMWO", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Searched database...", + } + ], + } + ], + }, + "output": { + "type": "rich_text", + "block_id": "viMWO", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Profile data loaded", + } + ], + } + ], + }, + }, + { + "type": "task_card", + "task_id": "call_002", + "title": "Checked user permissions", + "status": "pending", + }, + { + "type": "task_card", + "task_id": "call_003", + "title": "Generated comprehensive user report", + "status": "complete", + "output": { + "type": "rich_text", + "block_id": "crsk", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "15 data points compiled", + } + ], + } + ], + }, + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) diff --git a/block-kit/tests/blocks/test_task_card.py b/block-kit/tests/blocks/test_task_card.py new file mode 100644 index 0000000..802b924 --- /dev/null +++ b/block-kit/tests/blocks/test_task_card.py @@ -0,0 +1,41 @@ +import json + +from src.blocks import task_card + + +def test_example01(): + block = task_card.example01() + actual = block.to_dict() + expected = { + "type": "task_card", + "task_id": "task_1", + "title": "Fetching weather data", + "status": "pending", + "output": { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Found weather data for Chicago from 2 sources", + } + ], + } + ], + }, + "sources": [ + { + "type": "url", + "url": "https://weather.com/", + "text": "weather.com", + }, + { + "type": "url", + "url": "https://www.accuweather.com/", + "text": "accuweather.com", + }, + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True)