Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions slack_sdk/models/blocks/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
OptionGroup,
PlainTextObject,
RawTextObject,
TableBlockColumnSettings,
TextObject,
)
from .block_elements import (
Expand Down Expand Up @@ -92,6 +93,7 @@
"OptionGroup",
"PlainTextObject",
"RawTextObject",
"TableBlockColumnSettings",
"TextObject",
"BlockElement",
"ButtonElement",
Expand Down
40 changes: 40 additions & 0 deletions slack_sdk/models/blocks/basic_components.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,46 @@ def _validate_text_min_length(self):
return len(self.text) >= 1


class TableBlockColumnSettings(JsonObject):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧮 question: I'm curious if keeping this beside the TableBlock implementation is best? Or if it's right to keep here? AFAICT we won't share these settings with other blocks...

"""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
Expand Down
13 changes: 10 additions & 3 deletions slack_sdk/models/blocks/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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,
):
Expand Down
67 changes: 67 additions & 0 deletions tests/slack_sdk/models/test_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
SectionBlock,
StaticSelectElement,
TableBlock,
TableBlockColumnSettings,
TaskCardBlock,
VideoBlock,
)
Expand Down Expand Up @@ -1462,6 +1463,72 @@ 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=[{"type": "rich_text_section", "elements": [{"type": "text", "text": "Hello"}]}])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🪬 suggestion: Let's strengthen the parsing checks of this test with rich text elements too!

Suggested change
cell = RichTextBlock(elements=[{"type": "rich_text_section", "elements": [{"type": "text", "text": "Hello"}]}])
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
Expand Down
Loading