From 08d3e3e30e5a59321305c697c3e048a7a0ff96d0 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 31 Jul 2026 18:18:45 -0700 Subject: [PATCH] feat(block-kit): add container block example Add a container block example (example01) modeled on the container block reference page: a collapsible bulk-update summary grouping section, divider, context, and actions child blocks. The example references `import('@slack/types').ContainerBlock`, which is not yet exported by @slack/types, so `npm run check` (typecheck) fails by design. This is intentional: the example uses the real SDK type rather than hand-rolling one, so CI stays red until @slack/types ships ContainerBlock. Runtime tests and lint pass. Co-Authored-By: Claude --- block-kit/README.md | 1 + block-kit/src/blocks/container.js | 92 ++++++++++++++++++++++++ block-kit/tests/blocks/container.test.js | 84 ++++++++++++++++++++++ 3 files changed, 177 insertions(+) create mode 100644 block-kit/src/blocks/container.js create mode 100644 block-kit/tests/blocks/container.test.js diff --git a/block-kit/README.md b/block-kit/README.md index 8f64c51..6ec391d 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -12,6 +12,7 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes - **[Alert](https://docs.slack.dev/reference/block-kit/blocks/alert-block)**: Displays alerts, warnings, and informational messages. [Implementation](./src/blocks/alert.js). - **[Card](https://docs.slack.dev/reference/block-kit/blocks/card-block)**: Displays content in a card. [Implementation](./src/blocks/card.js). - **[Carousel](https://docs.slack.dev/reference/block-kit/blocks/carousel-block)**: Displays related card blocks in a horizontally-scrolling container. [Implementation](./src/blocks/carousel.js). +- **[Container](https://docs.slack.dev/reference/block-kit/blocks/container-block)**: Groups child blocks together in a configurable container. [Implementation](./src/blocks/container.js). - **[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.js). - **[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.js). - **[Divider](https://docs.slack.dev/reference/block-kit/blocks/divider-block)**: Visually separates pieces of info inside of a message. [Implementation](./src/blocks/divider.js). diff --git a/block-kit/src/blocks/container.js b/block-kit/src/blocks/container.js new file mode 100644 index 0000000..a427874 --- /dev/null +++ b/block-kit/src/blocks/container.js @@ -0,0 +1,92 @@ +/** + * A general-purpose wrapper for grouping child blocks together, with a configurable size. + * + * @see {@link https://docs.slack.dev/reference/block-kit/blocks/container-block/} + */ + +/** + * A collapsible container grouping a bulk-update summary out of section, + * divider, context, and actions child blocks. + * + * @returns {import('@slack/types').ContainerBlock} + */ +export function example01() { + /** + * @type {import('@slack/types').ContainerBlock} + */ + const block = { + type: "container", + block_id: "bkb_container_bulk_update", + title: { + type: "plain_text", + text: "Bulk update: 2 records selected", + }, + subtitle: { + type: "plain_text", + text: "Review changes before confirming", + }, + is_collapsible: true, + child_blocks: [ + { + type: "section", + block_id: "record-row-1", + text: { + type: "mrkdwn", + text: "*DCW-1024*\nStatus: Open → Closed\nAssignee: @princessdonut → @carl", + }, + }, + { + type: "divider", + block_id: "bulk-div-1", + }, + { + type: "section", + block_id: "record-row-2", + text: { + type: "mrkdwn", + text: "*DCW-1025*\nStatus: In Progress → Closed\nAssignee: @mordecai → @carl", + }, + }, + { + type: "divider", + block_id: "bulk-div-2", + }, + { + type: "context", + block_id: "bulk-status-bar", + elements: [ + { + type: "mrkdwn", + text: ":white_check_mark: 2 records will be updated • Status → Closed • Assignee → @carl", + }, + ], + }, + { + type: "actions", + block_id: "bulk-actions", + elements: [ + { + type: "button", + text: { + type: "plain_text", + text: "Confirm All", + emoji: true, + }, + style: "primary", + action_id: "bulk_confirm", + }, + { + type: "button", + text: { + type: "plain_text", + text: "Cancel", + emoji: true, + }, + action_id: "bulk_cancel", + }, + ], + }, + ], + }; + return block; +} diff --git a/block-kit/tests/blocks/container.test.js b/block-kit/tests/blocks/container.test.js new file mode 100644 index 0000000..c5d0048 --- /dev/null +++ b/block-kit/tests/blocks/container.test.js @@ -0,0 +1,84 @@ +import * as assert from "node:assert"; +import { describe, it } from "node:test"; +import { example01 } from "../../src/blocks/container.js"; + +describe("container", () => { + it("example01", () => { + const block = example01(); + const expected = { + type: "container", + block_id: "bkb_container_bulk_update", + title: { + type: "plain_text", + text: "Bulk update: 2 records selected", + }, + subtitle: { + type: "plain_text", + text: "Review changes before confirming", + }, + is_collapsible: true, + child_blocks: [ + { + type: "section", + block_id: "record-row-1", + text: { + type: "mrkdwn", + text: "*DCW-1024*\nStatus: Open → Closed\nAssignee: @princessdonut → @carl", + }, + }, + { + type: "divider", + block_id: "bulk-div-1", + }, + { + type: "section", + block_id: "record-row-2", + text: { + type: "mrkdwn", + text: "*DCW-1025*\nStatus: In Progress → Closed\nAssignee: @mordecai → @carl", + }, + }, + { + type: "divider", + block_id: "bulk-div-2", + }, + { + type: "context", + block_id: "bulk-status-bar", + elements: [ + { + type: "mrkdwn", + text: ":white_check_mark: 2 records will be updated • Status → Closed • Assignee → @carl", + }, + ], + }, + { + type: "actions", + block_id: "bulk-actions", + elements: [ + { + type: "button", + text: { + type: "plain_text", + text: "Confirm All", + emoji: true, + }, + style: "primary", + action_id: "bulk_confirm", + }, + { + type: "button", + text: { + type: "plain_text", + text: "Cancel", + emoji: true, + }, + action_id: "bulk_cancel", + }, + ], + }, + ], + }; + assert.deepStrictEqual(block, expected); + }); +});