diff --git a/slack_sdk/models/blocks/__init__.py b/slack_sdk/models/blocks/__init__.py index 6a26ed958..13c50c679 100644 --- a/slack_sdk/models/blocks/__init__.py +++ b/slack_sdk/models/blocks/__init__.py @@ -17,6 +17,7 @@ OptionGroup, PlainTextObject, RawTextObject, + TableBlockColumnSettings, TextObject, ) from .block_elements import ( @@ -92,6 +93,7 @@ "OptionGroup", "PlainTextObject", "RawTextObject", + "TableBlockColumnSettings", "TextObject", "BlockElement", "ButtonElement", diff --git a/slack_sdk/models/blocks/basic_components.py b/slack_sdk/models/blocks/basic_components.py index b6e71683a..6daffe067 100644 --- a/slack_sdk/models/blocks/basic_components.py +++ b/slack_sdk/models/blocks/basic_components.py @@ -191,6 +191,46 @@ def _validate_text_min_length(self): return len(self.text) >= 1 +class TableBlockColumnSettings(JsonObject): + """Column settings for TableBlock columns.""" + + @property + def attributes(self) -> Set[str]: + return {"align", "is_wrapped"} + + def __init__( + self, + *, + align: Optional[str] = None, + is_wrapped: Optional[bool] = None, + **others: dict, + ): + """Settings for a single column in a table block. + https://docs.slack.dev/reference/block-kit/blocks/table-block + + Args: + align: The alignment for items in this column. Can be "left", "center", or "right". + Defaults to "left" if not defined. + is_wrapped: Whether the contents of this column should be wrapped or not. + Defaults to false if not defined. + """ + show_unknown_key_warning(self, others) + self.align = align + self.is_wrapped = is_wrapped + + @classmethod + def parse( + cls, settings: Optional[Union[Dict[str, Any], "TableBlockColumnSettings"]] + ) -> Optional["TableBlockColumnSettings"]: + if settings is None: + return None + if isinstance(settings, TableBlockColumnSettings): + return settings + if isinstance(settings, dict): + return TableBlockColumnSettings(**settings) + return None + + class Option(JsonObject): """Option object used in dialogs, legacy message actions (interactivity in attachments), and blocks. JSON must be retrieved with an explicit option_type - the Slack API has diff --git a/slack_sdk/models/blocks/blocks.py b/slack_sdk/models/blocks/blocks.py index 46ed040a2..90d846790 100644 --- a/slack_sdk/models/blocks/blocks.py +++ b/slack_sdk/models/blocks/blocks.py @@ -7,7 +7,14 @@ from slack_sdk.models.basic_objects import JsonObject, JsonValidator from ...errors import SlackObjectFormationError -from .basic_components import MarkdownTextObject, PlainTextObject, SlackFile, TextObject +from .basic_components import ( + MarkdownTextObject, + PlainTextObject, + RawTextObject, + SlackFile, + TableBlockColumnSettings, + TextObject, +) from .block_elements import ( BlockElement, FeedbackButtonsElement, @@ -756,8 +763,8 @@ def attributes(self) -> Set[str]: # type: ignore[override] def __init__( self, *, - rows: Sequence[Sequence[Dict[str, Any]]], - column_settings: Optional[Sequence[Optional[Dict[str, Any]]]] = None, + rows: Sequence[Sequence[Union[Dict[str, Any], "RawTextObject", "RichTextBlock"]]], + column_settings: Optional[Sequence[Optional[Union[Dict[str, Any], "TableBlockColumnSettings"]]]] = None, block_id: Optional[str] = None, **others: dict, ): diff --git a/tests/slack_sdk/models/test_blocks.py b/tests/slack_sdk/models/test_blocks.py index ba0d378b1..a7513aecb 100644 --- a/tests/slack_sdk/models/test_blocks.py +++ b/tests/slack_sdk/models/test_blocks.py @@ -35,11 +35,12 @@ SectionBlock, StaticSelectElement, TableBlock, + TableBlockColumnSettings, TaskCardBlock, VideoBlock, ) from slack_sdk.models.blocks.basic_components import FeedbackButtonObject, SlackFile -from slack_sdk.models.blocks.block_elements import FeedbackButtonsElement, IconButtonElement, ImageElement +from slack_sdk.models.blocks.block_elements import FeedbackButtonsElement, IconButtonElement from . import STRING_3001_CHARS @@ -1462,6 +1463,74 @@ def test_with_block_id(self): } self.assertDictEqual(input, TableBlock(**input).to_dict()) + def test_with_column_settings_objects(self): + """Test table using typed TableBlockColumnSettings objects""" + block = TableBlock( + rows=[[{"type": "raw_text", "text": "A"}, {"type": "raw_text", "text": "B"}]], + column_settings=[ + TableBlockColumnSettings(align="right", is_wrapped=True), + TableBlockColumnSettings(align="left"), + ], + ) + expected = { + "type": "table", + "column_settings": [{"align": "right", "is_wrapped": True}, {"align": "left"}], + "rows": [[{"type": "raw_text", "text": "A"}, {"type": "raw_text", "text": "B"}]], + } + self.assertDictEqual(expected, block.to_dict()) + + def test_with_rich_text_cell_objects(self): + """Test table using typed RichTextBlock objects""" + cell = RichTextBlock( + elements=[RichTextSectionElement(elements=[RichTextElementParts.Text(text="Hello")])] + ) + block = TableBlock( + rows=[ + [RawTextObject(text="Header"), cell], + ], + ) + expected = { + "type": "table", + "rows": [ + [ + {"type": "raw_text", "text": "Header"}, + { + "type": "rich_text", + "elements": [{"type": "rich_text_section", "elements": [{"type": "text", "text": "Hello"}]}], + }, + ] + ], + } + self.assertDictEqual(expected, block.to_dict()) + + def test_mixed_typed_and_dict_cells(self): + """Test table accepts a mix of typed objects and plain dicts""" + block = TableBlock( + rows=[ + [RawTextObject(text="Col A"), RawTextObject(text="Col B")], + [ + {"type": "raw_text", "text": "Data"}, + RichTextBlock(elements=[{"type": "rich_text_section", "elements": [{"type": "text", "text": "rich"}]}]), + ], + ], + column_settings=[TableBlockColumnSettings(align="left"), {"align": "right"}], + ) + expected = { + "type": "table", + "column_settings": [{"align": "left"}, {"align": "right"}], + "rows": [ + [{"type": "raw_text", "text": "Col A"}, {"type": "raw_text", "text": "Col B"}], + [ + {"type": "raw_text", "text": "Data"}, + { + "type": "rich_text", + "elements": [{"type": "rich_text_section", "elements": [{"type": "text", "text": "rich"}]}], + }, + ], + ], + } + self.assertDictEqual(expected, block.to_dict()) + def test_column_settings_variations(self): """Test various column_settings configurations""" # Left align