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
5 changes: 5 additions & 0 deletions .changeset/add-container-block.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@slack/types": minor
---

feat(types): add `ContainerBlock` interface and `ContainerBlockChildBlock` union type
64 changes: 64 additions & 0 deletions packages/types/src/block-kit/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export type KnownBlock =
| AlertBlock
| CardBlock
| CarouselBlock
| ContainerBlock
| ContextBlock
| ContextActionsBlock
| DividerBlock
Expand Down Expand Up @@ -184,6 +185,69 @@ export interface CarouselBlock extends Block {
elements: CardBlock[];
}

/**
* @description A general-purpose wrapper for grouping child blocks together, with a configurable size.
* Note: `has_header_divider` cannot be set to `true` when `is_collapsible` is `true`.
* @see {@link https://docs.slack.dev/reference/block-kit/blocks/container-block Container block reference}.
*/
export interface ContainerBlock extends Block {
/**
* @description The type of block. For a container block, `type` is always `container`.
*/
type: 'container';
/**
* @description Plain text title for the container. Maximum length is 150 characters.
* One of `title` or `rich_text_title` is required.
*/
title?: PlainTextElement;
/**
* @description Rich text title for the container. Takes precedence over `title` if both are provided.
* One of `title` or `rich_text_title` is required.
*/
rich_text_title?: RichTextBlock;
Comment on lines +198 to +207

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: We should update reference pages to match these types if it's correct?

/**
* @description Subtitle for the container in plain text or mrkdwn format. Maximum length is 150 characters.
*/
subtitle?: TextObject;
/**
* @description An array of child blocks. Maximum 10 blocks.
*/
child_blocks: (
| ActionsBlock
| ContextBlock
| DividerBlock
| FileBlock
| HeaderBlock
| ImageBlock
| InputBlock
| RichTextBlock
| SectionBlock
| TableBlock
| VideoBlock
)[];
/**
* @description Controls the width of the container. Defaults to `"standard"`.
*/
width?: 'narrow' | 'standard' | 'wide' | 'full';
Comment on lines +228 to +231

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.

🌟 praise: I think we should embrace enum more in ongoing iteration and expand it as needed. IIRC this hasn't been our practice forever but it offers a better experience in current releases with fast fixes onward as needed. Changing a string to enum is more difficult I fear...

/**
* @description An image element displayed alongside the title and subtitle.
*/
icon?: ImageElement;
/**
* @description Whether the container can be collapsed. Defaults to `false`.
*/
is_collapsible?: boolean;
/**
* @description Whether the container is collapsed by default. Requires `is_collapsible` to be `true`. Defaults to `false`.
*/
default_collapsed?: boolean;
/**
* @description Whether to show a visible border separating header from content.
* Only applies when `is_collapsible` is not `true`. Defaults to `false`.
*/
has_header_divider?: boolean;
}

/**
* A helper union type of all Block Elements that can be used in a {@link ContextBlock}.
* @see {@link https://docs.slack.dev/reference/block-kit/blocks/context-block Context block reference}.
Expand Down
42 changes: 41 additions & 1 deletion packages/types/test/blocks.test-d.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expectAssignable, expectError } from 'tsd';
import type { AlertBlock, CardBlock, CarouselBlock, KnownBlock } from '../src/index';
import type { AlertBlock, CardBlock, CarouselBlock, ContainerBlock, KnownBlock } from '../src/index';

// CardBlock
// -- sad path
Expand Down Expand Up @@ -62,3 +62,43 @@ expectAssignable<KnownBlock>({
type: 'carousel',
elements: [{ type: 'card' }],
});

// ContainerBlock
// -- sad path
expectError<ContainerBlock>({}); // missing type and child_blocks
expectError<ContainerBlock>({ type: 'container' }); // missing required child_blocks
// -- happy path
expectAssignable<ContainerBlock>({
type: 'container',
title: { type: 'plain_text', text: 'My Container' },
child_blocks: [{ type: 'divider' }],
});
expectAssignable<ContainerBlock>({
type: 'container',
title: { type: 'plain_text', text: 'Full Container' },
subtitle: { type: 'plain_text', text: 'A subtitle' },
child_blocks: [{ type: 'section', text: { type: 'mrkdwn', text: 'Content' } }, { type: 'divider' }],
width: 'wide',
icon: { type: 'image', image_url: 'https://example.com/icon.png', alt_text: 'icon' },
is_collapsible: true,
default_collapsed: true,
});
expectAssignable<ContainerBlock>({
type: 'container',
rich_text_title: {
type: 'rich_text',
elements: [{ type: 'rich_text_section', elements: [{ type: 'text', text: 'Rich Title' }] }],
},
child_blocks: [{ type: 'divider' }],
});
expectAssignable<ContainerBlock>({
type: 'container',
title: { type: 'plain_text', text: 'Divider' },
child_blocks: [{ type: 'divider' }],
has_header_divider: true,
});
expectAssignable<KnownBlock>({
type: 'container',
title: { type: 'plain_text', text: 'Known' },
child_blocks: [{ type: 'divider' }],
});