From 8da8f1ca0ca65c76d8b29b73166dfad6b181b129 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 26 Jun 2026 17:06:24 -0700 Subject: [PATCH 1/4] feat(block-kit): add data_table block example Mirrors the official Block Kit data_table docs example in the bolt-python-examples block-kit project: a captioned table with a header row and rich-text badge cells. The data_table block is net-new and not yet in a published slack_sdk release, so the example defines a DataTableBlock(Block) subclass that mirrors the SDK convention, matching the neighboring data_visualization example. Swap to the slack_sdk type once it ships. Docs: https://docs.slack.dev/reference/block-kit/blocks/data-table-block Co-Authored-By: Claude --- block-kit/README.md | 1 + block-kit/src/blocks/data_table.py | 127 ++++++++++++++++++++++ block-kit/tests/blocks/test_data_table.py | 78 +++++++++++++ 3 files changed, 206 insertions(+) create mode 100644 block-kit/src/blocks/data_table.py create mode 100644 block-kit/tests/blocks/test_data_table.py diff --git a/block-kit/README.md b/block-kit/README.md index ee77ff0..eba5e78 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -11,6 +11,7 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes - **[Actions](https://docs.slack.dev/reference/block-kit/blocks/actions-block)**: Holds multiple interactive elements. [Implementation](./src/blocks/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). +- **[Data table](https://docs.slack.dev/reference/block-kit/blocks/data-table-block)**: Displays data arranged in rows and columns with built-in pagination. [Implementation](./src/blocks/data_table.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). diff --git a/block-kit/src/blocks/data_table.py b/block-kit/src/blocks/data_table.py new file mode 100644 index 0000000..4d7d6f1 --- /dev/null +++ b/block-kit/src/blocks/data_table.py @@ -0,0 +1,127 @@ +from typing import Any, Dict, List, Optional, Set + +from slack_sdk.models.blocks import Block + + +class DataTableBlock(Block): + """Displays data arranged in rows and columns with built-in pagination. + https://docs.slack.dev/reference/block-kit/blocks/data-table-block + + The slack_sdk does not yet ship a typed class for this block, so this + example defines one that mirrors the SDK convention. + """ + + type = "data_table" + + @property + def attributes(self) -> Set[str]: # type: ignore[override] + return super().attributes.union( + {"rows", "caption", "page_size", "row_header_column_index"} + ) + + def __init__( + self, + *, + rows: List[List[Dict[str, Any]]], + caption: str, + page_size: Optional[int] = None, + row_header_column_index: Optional[int] = None, + block_id: Optional[str] = None, + ) -> None: + """ + Args: + rows (required): An array of rows, where each row is an array of cell + objects. Cells may be raw_text, raw_number, or rich_text. + caption (required): A label for the table, rendered as the HTML caption + element for accessibility. + page_size: The number of rows shown per page. Ranges from 1 to 100 and + defaults to 5. + row_header_column_index: The zero-based index of the column that + identifies row headers for accessibility. Defaults to 0. + block_id: A unique identifier for a block. If not specified, a block_id + will be generated. Maximum length for this field is 255 characters. + """ + super().__init__(type=self.type, block_id=block_id) + self.rows = rows + self.caption = caption + self.page_size = page_size + self.row_header_column_index = row_header_column_index + + +def example01() -> DataTableBlock: + """ + Displays data arranged in rows and columns with built-in pagination. + https://docs.slack.dev/reference/block-kit/blocks/data-table-block/ + + A table with a header row and three data rows. The first two columns hold + raw text, while the third column holds rich text with styled badges. + """ + block = DataTableBlock( + caption="A Fabulous Table", + rows=[ + [ + {"type": "raw_text", "text": "Name"}, + {"type": "raw_text", "text": "Department"}, + {"type": "raw_text", "text": "Badge"}, + ], + [ + {"type": "raw_text", "text": "Data Refinement Department"}, + {"type": "raw_text", "text": "MDR"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Blue", + "style": {"bold": True}, + } + ], + } + ], + }, + ], + [ + {"type": "raw_text", "text": "Art Sourcing Department"}, + {"type": "raw_text", "text": "O&D"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + {"type": "text", "text": "Green"}, + { + "type": "text", + "text": "review", + "style": {"italic": True}, + }, + ], + } + ], + }, + ], + [ + {"type": "raw_text", "text": "Wellness Department"}, + {"type": "raw_text", "text": "Wellness Center"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Limited", + "style": {"bold": True}, + } + ], + } + ], + }, + ], + ], + ) + return block diff --git a/block-kit/tests/blocks/test_data_table.py b/block-kit/tests/blocks/test_data_table.py new file mode 100644 index 0000000..9752d4e --- /dev/null +++ b/block-kit/tests/blocks/test_data_table.py @@ -0,0 +1,78 @@ +import json + +from src.blocks import data_table + + +def test_example01(): + block = data_table.example01() + actual = block.to_dict() + expected = { + "type": "data_table", + "caption": "A Fabulous Table", + "rows": [ + [ + {"type": "raw_text", "text": "Name"}, + {"type": "raw_text", "text": "Department"}, + {"type": "raw_text", "text": "Badge"}, + ], + [ + {"type": "raw_text", "text": "Data Refinement Department"}, + {"type": "raw_text", "text": "MDR"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Blue", + "style": {"bold": True}, + } + ], + } + ], + }, + ], + [ + {"type": "raw_text", "text": "Art Sourcing Department"}, + {"type": "raw_text", "text": "O&D"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + {"type": "text", "text": "Green"}, + { + "type": "text", + "text": "review", + "style": {"italic": True}, + }, + ], + } + ], + }, + ], + [ + {"type": "raw_text", "text": "Wellness Department"}, + {"type": "raw_text", "text": "Wellness Center"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Limited", + "style": {"bold": True}, + } + ], + } + ], + }, + ], + ], + } + assert json.dumps(actual, sort_keys=True) == json.dumps(expected, sort_keys=True) From 56fbcfc435f45ed7177de99de8759606723109fc Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 31 Jul 2026 18:46:18 -0700 Subject: [PATCH 2/4] refactor(block-kit): use slack_sdk DataTableBlock + model-class cells MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hand-rolled DataTableBlock(Block) subclass with an import of DataTableBlock from slack_sdk.models.blocks, and build the row cells with model classes (RawTextObject, RichTextBlock / RichTextSectionElement / RichTextElementParts) instead of raw dicts. slack_sdk does not yet export DataTableBlock, so both mypy and pytest (collection) fail by design — the example genuinely cannot run until the SDK ships the class, rather than a local subclass masking the gap. Ruff and formatting pass; the serialized shape is unchanged, so the existing to_dict() assertion stays valid once the class ships. Co-Authored-By: Claude --- block-kit/src/blocks/data_table.py | 144 ++++++++--------------------- 1 file changed, 40 insertions(+), 104 deletions(-) diff --git a/block-kit/src/blocks/data_table.py b/block-kit/src/blocks/data_table.py index 4d7d6f1..31a0dcc 100644 --- a/block-kit/src/blocks/data_table.py +++ b/block-kit/src/blocks/data_table.py @@ -1,126 +1,62 @@ -from typing import Any, Dict, List, Optional, Set +from slack_sdk.models.blocks import ( + DataTableBlock, + RawTextObject, + RichTextBlock, + RichTextElementParts, + RichTextSectionElement, +) -from slack_sdk.models.blocks import Block - -class DataTableBlock(Block): - """Displays data arranged in rows and columns with built-in pagination. - https://docs.slack.dev/reference/block-kit/blocks/data-table-block - - The slack_sdk does not yet ship a typed class for this block, so this - example defines one that mirrors the SDK convention. - """ - - type = "data_table" - - @property - def attributes(self) -> Set[str]: # type: ignore[override] - return super().attributes.union( - {"rows", "caption", "page_size", "row_header_column_index"} - ) - - def __init__( - self, - *, - rows: List[List[Dict[str, Any]]], - caption: str, - page_size: Optional[int] = None, - row_header_column_index: Optional[int] = None, - block_id: Optional[str] = None, - ) -> None: - """ - Args: - rows (required): An array of rows, where each row is an array of cell - objects. Cells may be raw_text, raw_number, or rich_text. - caption (required): A label for the table, rendered as the HTML caption - element for accessibility. - page_size: The number of rows shown per page. Ranges from 1 to 100 and - defaults to 5. - row_header_column_index: The zero-based index of the column that - identifies row headers for accessibility. Defaults to 0. - block_id: A unique identifier for a block. If not specified, a block_id - will be generated. Maximum length for this field is 255 characters. - """ - super().__init__(type=self.type, block_id=block_id) - self.rows = rows - self.caption = caption - self.page_size = page_size - self.row_header_column_index = row_header_column_index +def _rich_text(*elements: RichTextElementParts.Text) -> RichTextBlock: + """Wrap rich text elements in a rich_text cell for a data table.""" + return RichTextBlock(elements=[RichTextSectionElement(elements=list(elements))]) def example01() -> DataTableBlock: """ - Displays data arranged in rows and columns with built-in pagination. + Displays structured, paginated data in rows and columns. https://docs.slack.dev/reference/block-kit/blocks/data-table-block/ - A table with a header row and three data rows. The first two columns hold - raw text, while the third column holds rich text with styled badges. + A data table of departments with raw text and rich text cells. """ block = DataTableBlock( caption="A Fabulous Table", rows=[ [ - {"type": "raw_text", "text": "Name"}, - {"type": "raw_text", "text": "Department"}, - {"type": "raw_text", "text": "Badge"}, + RawTextObject(text="Name"), + RawTextObject(text="Department"), + RawTextObject(text="Badge"), ], [ - {"type": "raw_text", "text": "Data Refinement Department"}, - {"type": "raw_text", "text": "MDR"}, - { - "type": "rich_text", - "elements": [ - { - "type": "rich_text_section", - "elements": [ - { - "type": "text", - "text": "Blue", - "style": {"bold": True}, - } - ], - } - ], - }, + RawTextObject(text="Data Refinement Department"), + RawTextObject(text="MDR"), + _rich_text( + RichTextElementParts.Text( + text="Blue", + style=RichTextElementParts.TextStyle(bold=True), + ) + ), ], [ - {"type": "raw_text", "text": "Art Sourcing Department"}, - {"type": "raw_text", "text": "O&D"}, - { - "type": "rich_text", - "elements": [ - { - "type": "rich_text_section", - "elements": [ - {"type": "text", "text": "Green"}, - { - "type": "text", - "text": "review", - "style": {"italic": True}, - }, - ], - } - ], - }, + RawTextObject(text="Art Sourcing Department"), + RawTextObject(text="O&D"), + _rich_text( + RichTextElementParts.Text(text="Green"), + RichTextElementParts.Text( + text="review", + style=RichTextElementParts.TextStyle(italic=True), + ), + ), ], [ - {"type": "raw_text", "text": "Wellness Department"}, - {"type": "raw_text", "text": "Wellness Center"}, - { - "type": "rich_text", - "elements": [ - { - "type": "rich_text_section", - "elements": [ - { - "type": "text", - "text": "Limited", - "style": {"bold": True}, - } - ], - } - ], - }, + RawTextObject(text="Wellness Department"), + RawTextObject(text="Wellness Center"), + _rich_text( + RichTextElementParts.Text( + text="Limited", + style=RichTextElementParts.TextStyle(bold=True), + ) + ), ], ], ) From 9cf67b7ec2de6a9e0e507ab3b67d5ec02de7df02 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 31 Jul 2026 18:48:09 -0700 Subject: [PATCH 3/4] refactor(block-kit): use slack_sdk DataTableBlock, keep dict cells, no helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the hand-rolled DataTableBlock(Block) subclass with an import of DataTableBlock from slack_sdk.models.blocks. Keep the row cells as plain dicts (matching the sibling table example, whose TableBlock.rows is typed as Sequence[Sequence[Dict[str, Any]]]) and drop the local helper — the example stays flat and copy-pasteable. slack_sdk does not yet export DataTableBlock, so mypy and pytest fail by design on the missing import (not on cell shapes) — the honest signal that the block is not yet available, unblocking when the SDK ships it. Co-Authored-By: Claude --- block-kit/src/blocks/data_table.py | 96 +++++++++++++++++------------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/block-kit/src/blocks/data_table.py b/block-kit/src/blocks/data_table.py index 31a0dcc..e7b0c2f 100644 --- a/block-kit/src/blocks/data_table.py +++ b/block-kit/src/blocks/data_table.py @@ -1,15 +1,4 @@ -from slack_sdk.models.blocks import ( - DataTableBlock, - RawTextObject, - RichTextBlock, - RichTextElementParts, - RichTextSectionElement, -) - - -def _rich_text(*elements: RichTextElementParts.Text) -> RichTextBlock: - """Wrap rich text elements in a rich_text cell for a data table.""" - return RichTextBlock(elements=[RichTextSectionElement(elements=list(elements))]) +from slack_sdk.models.blocks import DataTableBlock def example01() -> DataTableBlock: @@ -23,40 +12,67 @@ def example01() -> DataTableBlock: caption="A Fabulous Table", rows=[ [ - RawTextObject(text="Name"), - RawTextObject(text="Department"), - RawTextObject(text="Badge"), + {"type": "raw_text", "text": "Name"}, + {"type": "raw_text", "text": "Department"}, + {"type": "raw_text", "text": "Badge"}, ], [ - RawTextObject(text="Data Refinement Department"), - RawTextObject(text="MDR"), - _rich_text( - RichTextElementParts.Text( - text="Blue", - style=RichTextElementParts.TextStyle(bold=True), - ) - ), + {"type": "raw_text", "text": "Data Refinement Department"}, + {"type": "raw_text", "text": "MDR"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Blue", + "style": {"bold": True}, + } + ], + } + ], + }, ], [ - RawTextObject(text="Art Sourcing Department"), - RawTextObject(text="O&D"), - _rich_text( - RichTextElementParts.Text(text="Green"), - RichTextElementParts.Text( - text="review", - style=RichTextElementParts.TextStyle(italic=True), - ), - ), + {"type": "raw_text", "text": "Art Sourcing Department"}, + {"type": "raw_text", "text": "O&D"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + {"type": "text", "text": "Green"}, + { + "type": "text", + "text": "review", + "style": {"italic": True}, + }, + ], + } + ], + }, ], [ - RawTextObject(text="Wellness Department"), - RawTextObject(text="Wellness Center"), - _rich_text( - RichTextElementParts.Text( - text="Limited", - style=RichTextElementParts.TextStyle(bold=True), - ) - ), + {"type": "raw_text", "text": "Wellness Department"}, + {"type": "raw_text", "text": "Wellness Center"}, + { + "type": "rich_text", + "elements": [ + { + "type": "rich_text_section", + "elements": [ + { + "type": "text", + "text": "Limited", + "style": {"bold": True}, + } + ], + } + ], + }, ], ], ) From 906b5174163ffad5c3c71133ad00bcc2d799b3e3 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 4 Aug 2026 21:45:54 -0700 Subject: [PATCH 4/4] fix(data_table): match description to docs.slack.dev wording Co-Authored-By: Claude --- block-kit/src/blocks/data_table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block-kit/src/blocks/data_table.py b/block-kit/src/blocks/data_table.py index e7b0c2f..9cc15fb 100644 --- a/block-kit/src/blocks/data_table.py +++ b/block-kit/src/blocks/data_table.py @@ -3,7 +3,7 @@ def example01() -> DataTableBlock: """ - Displays structured, paginated data in rows and columns. + Displays rich tables that support pagination, sorting, filtering, and interactivity. https://docs.slack.dev/reference/block-kit/blocks/data-table-block/ A data table of departments with raw text and rich text cells.