Skip to content

feat(blocks): add Card, Carousel, and Alert block types#1865

Merged
srtaalej merged 7 commits intomainfrom
ale-new-block-kit-elements
May 5, 2026
Merged

feat(blocks): add Card, Carousel, and Alert block types#1865
srtaalej merged 7 commits intomainfrom
ale-new-block-kit-elements

Conversation

@srtaalej
Copy link
Copy Markdown
Contributor

@srtaalej srtaalej commented Apr 29, 2026

Summary

  • Adds CardBlock — a rich display block for presenting structured content (hero image, icon, title, subtitle, body, actions)
  • Adds CarouselBlock — a horizontally scrollable collection of card blocks (1–10 cards)
  • Adds AlertBlock — a prominent notice block for warnings, status updates, or important information with severity levels (default, info, warning, error, success)

All three blocks include deserialization support via Block.parse(), exports in __init__.py, and round-trip tests.

Note: The AlertBlock is only supported in modal views (views_open / views_update). It is not supported in chat_postMessage or other message surfaces.

Test plan

Test app.py (sends carousel + cards via chat_postMessage on startup)
import os
import logging

from slack_bolt import App
from slack_bolt.adapter.socket_mode import SocketModeHandler
from slack_sdk.models.blocks import CarouselBlock, CardBlock, ButtonElement, ImageElement

from listeners import register_listeners

logging.basicConfig(level=logging.DEBUG)

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

# Register Listeners
register_listeners(app)

# Start Bolt app
if __name__ == "__main__":
    app.client.chat_postMessage(
        channel="#test-blockkit-new-types",
        blocks=[
            CarouselBlock(elements=[
                CardBlock(
                    hero_image=ImageElement(image_url="https://picsum.photos/seed/a/400/200", alt_text="Card 1 hero"),
                    title="Card Block",
                    subtitle="Rich structured content",
                    body="Recommendations, results, or work items.",
                    actions=[ButtonElement(text="Learn more", action_id="card_1_btn")],
                ),
                CardBlock(
                    hero_image=ImageElement(image_url="https://picsum.photos/seed/b/400/200", alt_text="Card 2 hero"),
                    title="Alert Block",
                    subtitle="Prominent notices",
                    body="Warnings, status updates, and more.",
                    actions=[ButtonElement(text="Learn more", action_id="card_2_btn")],
                ),
                CardBlock(
                    hero_image=ImageElement(image_url="https://picsum.photos/seed/c/400/200", alt_text="Card 3 hero"),
                    title="Carousel Block",
                    subtitle="Scrollable card collections",
                    body="Up to 10 cards, side by side.",
                    actions=[ButtonElement(text="Learn more", action_id="card_3_btn")],
                ),
            ]),
        ],
        text="New Block Kit types demo",
    )
    SocketModeHandler(app, os.environ.get("SLACK_APP_TOKEN")).start()
Test shortcut handler (opens modal with alert blocks via global shortcut)
def sample_shortcut_callback(body: dict, ack: Ack, client: WebClient, logger: Logger):
    try:
        ack()
        blocks = [
            AlertBlock(text="This is a *default* alert block").to_dict(),
            AlertBlock(text="This is an *info* alert block", level="info").to_dict(),
            AlertBlock(text="This is a *warning* alert block", level="warning").to_dict(),
            AlertBlock(text="This is an *error* alert block", level="error").to_dict(),
            AlertBlock(text="This is a *success* alert block", level="success").to_dict(),
            {
                "type": "input",
                "block_id": "input_block_id",
                "label": {
                    "type": "plain_text",
                    "text": "What are your hopes and dreams?",
                },
                "element": {
                    "type": "plain_text_input",
                    "action_id": "sample_input_id",
                    "multiline": True,
                },
            },
            {
                "block_id": "select_channel_block_id",
                "type": "input",
                "label": {
                    "type": "plain_text",
                    "text": "Select a channel to message the result to",
                },
                "element": {
                    "type": "conversations_select",
                    "action_id": "sample_dropdown_id",
                    "response_url_enabled": True,
                },
            },
        ]
        client.views_open(
            trigger_id=body["trigger_id"],
            view={
                "type": "modal",
                "callback_id": "sample_view_id",
                "title": {"type": "plain_text", "text": "Alert Block Demo"},
                "blocks": blocks,
                "submit": {"type": "plain_text", "text": "Submit"},
            },
        )
    except Exception as e:
        logger.error(e)
  • Round-trip serialization tests for all three block types
  • Block.parse() deserialization tests
  • Validation tests (required fields, length constraints, enum values)
  • Integration test: send a message with CardBlock to Slack
  • Integration test: send a message with CarouselBlock to Slack
  • Integration test: open a modal with AlertBlock in Slack (alert blocks are only supported in modal views)

🤖 Generated with Claude Code

@srtaalej srtaalej self-assigned this Apr 29, 2026
@srtaalej srtaalej added enhancement M-T: A feature request for new functionality semver:minor server-side-issue labels Apr 29, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 29, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.09%. Comparing base (60bd43d) to head (2140576).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1865      +/-   ##
==========================================
+ Coverage   84.00%   84.09%   +0.08%     
==========================================
  Files         117      117              
  Lines       13257    13325      +68     
==========================================
+ Hits        11137    11205      +68     
  Misses       2120     2120              

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

@srtaalej srtaalej marked this pull request as ready for review April 30, 2026 20:00
@srtaalej srtaalej requested a review from a team as a code owner April 30, 2026 20:00
@zimeg
Copy link
Copy Markdown
Member

zimeg commented May 4, 2026

@srtaalej Might I request an example snippet using the CarouselBlock imports? I'm hoping we can put this effort toward the slack-samples/bolt-python-examples project after release 📚

@srtaalej
Copy link
Copy Markdown
Contributor Author

srtaalej commented May 4, 2026

@srtaalej Might I request an example snippet using the CarouselBlock imports? I'm hoping we can put this effort toward the slack-samples/bolt-python-examples project after release 📚

ah yes ofc!

@zimeg zimeg added this to the 3.41.1 milestone May 4, 2026
Copy link
Copy Markdown
Member

@zimeg zimeg left a comment

Choose a reason for hiding this comment

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

@srtaalej LGTM! Thanks for adding the example code! Those imports make a big difference in testing 🎁 ✨

I'm approving now with a few notes but nothing too important:

  • Let's alphabeticize these new additions if ordering allows!
  • Our inline docs become more different from the API reference in some of these descriptions

The second point might require wider changes across blocks but I'm wanting to know if we're aligned with docs for some of these block shapes? 🪬

Comment thread slack_sdk/models/blocks/blocks.py Outdated
return self.body is None or self.body.text is None or len(self.body.text) <= self.body_max_length


class AlertBlock(Block):
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: If dependencies aren't required this order, can we use applied alphabetics on these blocks?

Comment thread slack_sdk/models/blocks/blocks.py Outdated
Comment thread slack_sdk/models/blocks/blocks.py Outdated
Comment on lines +912 to +913
hero_image: Optional[Union[dict, ImageElement]] = None,
icon: Optional[Union[dict, ImageElement]] = None,
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 notice the current docs suggest string but we're finding the image block is expected? I'm curious if the docs team has seen these differences with this changeset?

Comment thread slack_sdk/models/blocks/blocks.py Outdated
def __init__(
self,
*,
elements: Sequence[Union[dict, "CardBlock"]],
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.

Suggested change
elements: Sequence[Union[dict, "CardBlock"]],
elements: Sequence[Union[dict, CardBlock]],

👾 question(non-blocking): Does the stringified class avoid formatting issue or can we reference this direct?

@zimeg
Copy link
Copy Markdown
Member

zimeg commented May 4, 2026

🔮 note: The release milestone 3.41.1 should be 3.42.0 or "next" but we can adjust this at time of release for this change!

@srtaalej srtaalej merged commit 73d255a into main May 5, 2026
18 checks passed
@srtaalej srtaalej deleted the ale-new-block-kit-elements branch May 5, 2026 15:26
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 semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants