Skip to content

feat(models): add ContainerBlock for block kit - #1949

Open
srtaalej wants to merge 2 commits into
mainfrom
ale-add-containerblock
Open

feat(models): add ContainerBlock for block kit#1949
srtaalej wants to merge 2 commits into
mainfrom
ale-add-containerblock

Conversation

@srtaalej

@srtaalej srtaalej commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds ContainerBlock (type: "container") to the Block Kit models per https://docs.slack.dev/reference/block-kit/blocks/container-block/
  • Supports all documented properties: title, rich_text_title, subtitle, child_blocks, width, icon, is_collapsible, default_collapsed, has_header_divider
  • Includes validators for title/rich_text_title presence, max lengths, child_blocks count, width enum, and mutual exclusion of is_collapsible + has_header_divider

Test plan

  • Full existing test suite passes (99 tests, 0 regressions)
  • Manually tested against the Slack API via a Bolt test app (see below)
Example Bolt test app (app.py)

Register a /container-block slash command in your test app and use the text argument to test different property combinations:

  • /container-block — base (title + child_blocks)
  • /container-block width — adds width: "wide"
  • /container-block mrkdwn-subtitle — subtitle with mrkdwn type
  • /container-block has-header-divider — adds has_header_divider: true
  • /container-block default-collapsed — adds is_collapsible + default_collapsed
  • /container-block icon — adds icon image element
  • /container-block rich-text-title — uses rich_text_title instead of title
  • /container-block all — all compatible properties together
import logging
import os

from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler

logging.basicConfig(level=logging.DEBUG)

app = App(token=os.environ.get("SLACK_BOT_TOKEN"))


@app.command("/container-block")
def container_block_command(ack, respond, command):
    ack()
    variant = command.get("text", "").strip()

    base = {
        "type": "container",
        "title": {"type": "plain_text", "text": "Container Block Test"},
        "child_blocks": [
            {"type": "section", "text": {"type": "mrkdwn", "text": "Section inside a container."}},
            {"type": "divider"},
            {"type": "section", "text": {"type": "mrkdwn", "text": "Another section below the divider."}},
        ],
    }

    if variant == "width":
        base["width"] = "wide"
    elif variant == "mrkdwn-subtitle":
        base["subtitle"] = {"type": "mrkdwn", "text": "*Bold* subtitle"}
    elif variant == "has-header-divider":
        base["has_header_divider"] = True
    elif variant == "default-collapsed":
        base["is_collapsible"] = True
        base["default_collapsed"] = True
    elif variant == "icon":
        base["icon"] = {
            "type": "image",
            "image_url": "https://api.slack.com/img/blocks/bkb_template_images/plants.png",
            "alt_text": "icon",
        }
    elif variant == "rich-text-title":
        del base["title"]
        base["rich_text_title"] = {
            "type": "rich_text",
            "elements": [
                {"type": "rich_text_section", "elements": [{"type": "text", "text": "Rich Title"}]},
            ],
        }
    elif variant == "all":
        base["subtitle"] = {"type": "plain_text", "text": "All compatible properties"}
        base["width"] = "wide"
        base["icon"] = {
            "type": "image",
            "image_url": "https://api.slack.com/img/blocks/bkb_template_images/plants.png",
            "alt_text": "icon",
        }
        base["is_collapsible"] = True
    else:
        base["subtitle"] = {
            "type": "plain_text",
            "text": "Base test (try: width, mrkdwn-subtitle, has-header-divider, default-collapsed, icon, rich-text-title, all)",
        }

    respond(blocks=[base])


if __name__ == "__main__":
    SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start()

@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.08%. Comparing base (49101ef) to head (f9411e8).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1949      +/-   ##
==========================================
+ Coverage   84.06%   84.08%   +0.02%     
==========================================
  Files         118      118              
  Lines       13506    13550      +44     
==========================================
+ Hits        11354    11394      +40     
- Misses       2152     2156       +4     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@srtaalej srtaalej self-assigned this Aug 31, 2026
@srtaalej srtaalej added enhancement M-T: A feature request for new functionality semver:minor models labels Aug 31, 2026
@srtaalej
srtaalej marked this pull request as ready for review August 31, 2026 16:27
@srtaalej
srtaalej requested a review from a team as a code owner August 31, 2026 16:27

@zimeg zimeg left a comment

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.

@srtaalej LGTM! Thanks for getting this shipped! 🎁 🚢 💨

Comment on lines +1096 to +1098
@JsonValidator("width must be a valid value (narrow, standard, wide, full)")
def _validate_width(self):
return self.width is None or self.width in self.valid_widths

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.

🌊 note: These validation patterns sometimes concern me if the API changes what's accepted but no blocker for the current patterns available.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement M-T: A feature request for new functionality models semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants