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
77 changes: 77 additions & 0 deletions packages/types/src/block-kit/blocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import type {
WorkflowButton,
} from './block-elements';
import type {
MrkdwnElement,
PlainTextElement,
RawTextElement,
SlackFileImageObject,
Expand Down Expand Up @@ -54,6 +55,9 @@ export interface Block {
*/
export type KnownBlock =
| ActionsBlock
| AlertBlock
| CardBlock
| CarouselBlock
| ContextBlock
| ContextActionsBlock
| DividerBlock
Expand Down Expand Up @@ -467,6 +471,79 @@ export interface PlanBlock extends Block {
tasks?: (TaskCardBlock | Record<string, unknown>)[];
}

/**
* @description A rich display block for presenting structured content such as recommendations, results, or work items.
* At least one of `hero_image`, `title`, `actions`, or `body` must be provided.
* @see {@link https://docs.slack.dev/reference/block-kit/blocks/card-block Card block reference}.
*/
export interface CardBlock extends Block {
/**
* @description The type of block. For a card block, `type` is always `card`.
*/
type: 'card';
/**
* @description A top banner image for the card in the form of an {@link ImageElement}.
*/
hero_image?: ImageElement;
/**
* @description A small icon displayed next to the title and subtitle in the form of an {@link ImageElement}.
*/
icon?: ImageElement;
/**
* @description The title of the card in the form of a {@link MrkdwnElement}.
* Maximum length for the text in this field is 150 characters.
*/
title?: MrkdwnElement;
/**
* @description The subtitle of the card in the form of a {@link MrkdwnElement}.
* Maximum length for the text in this field is 150 characters.
*/
subtitle?: MrkdwnElement;
/**
* @description The body text of the card in the form of a {@link MrkdwnElement}.
* Maximum length for the text in this field is 200 characters.
*/
body?: MrkdwnElement;
/**
* @description An array of {@link Button} elements displayed at the bottom of the card.
*/
actions?: Button[];
}

/**
* @description A prominent notice block for displaying warnings, status updates, or other important information.
* @see {@link https://docs.slack.dev/reference/block-kit/blocks/alert-block Alert block reference}.
*/
export interface AlertBlock extends Block {
/**
* @description The type of block. For an alert block, `type` is always `alert`.
*/
type: 'alert';
/**
* @description The alert message content in the form of a {@link TextObject}.
*/
text: TextObject;
/**
* @description The severity level of the alert. Defaults to `"default"` if omitted.
*/
level?: 'default' | 'info' | 'warning' | 'error' | 'success';
}

/**
* @description A horizontally scrollable collection of {@link CardBlock} elements.
* @see {@link https://docs.slack.dev/reference/block-kit/blocks/carousel-block Carousel block reference}.
*/
export interface CarouselBlock extends Block {
/**
* @description The type of block. For a carousel block, `type` is always `carousel`.
*/
type: 'carousel';
/**
* @description An array of {@link CardBlock} elements. Minimum 1, maximum 10 cards.
*/
elements: CardBlock[];
}

/**
* @description Displays an embedded video player. A video block is designed to embed videos in all app surfaces (e.g.
* link unfurls, messages, modals, App Home) — anywhere you can put blocks! To use the video block within your app, you
Expand Down
64 changes: 64 additions & 0 deletions packages/types/test/blocks.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { expectAssignable, expectError } from 'tsd';
import type { AlertBlock, CardBlock, CarouselBlock, KnownBlock } from '../src/index';

// CardBlock
// -- sad path
expectError<CardBlock>({}); // missing type
expectError<CardBlock>({ type: 'card', title: { type: 'plain_text', text: 'wrong type' } }); // title must be mrkdwn
// -- happy path
expectAssignable<CardBlock>({ type: 'card' });
expectAssignable<CardBlock>({
type: 'card',
title: { type: 'mrkdwn', text: 'Title' },
});
expectAssignable<CardBlock>({
type: 'card',
icon: { type: 'image', image_url: 'https://example.com/icon.png', alt_text: 'icon' },
title: { type: 'mrkdwn', text: 'Lumon Industries' },
subtitle: { type: 'mrkdwn', text: 'Committed to work-life balance' },
hero_image: { type: 'image', image_url: 'https://example.com/hero.png', alt_text: 'hero' },
body: { type: 'mrkdwn', text: 'Please enjoy each card equally.' },
actions: [{ type: 'button', text: { type: 'plain_text', text: 'Click' }, action_id: 'btn' }],
});
expectAssignable<KnownBlock>({ type: 'card', body: { type: 'mrkdwn', text: 'hi' } });

// AlertBlock
// -- sad path
expectError<AlertBlock>({}); // missing type and text
expectError<AlertBlock>({ type: 'alert' }); // missing required text
// -- happy path
expectAssignable<AlertBlock>({
type: 'alert',
text: { type: 'mrkdwn', text: 'Something happened' },
});
expectAssignable<AlertBlock>({
type: 'alert',
text: { type: 'plain_text', text: 'Simple alert' },
level: 'warning',
});
expectAssignable<KnownBlock>({
type: 'alert',
text: { type: 'mrkdwn', text: 'Notice' },
level: 'error',
});

// CarouselBlock
// -- sad path
expectError<CarouselBlock>({}); // missing type and elements
expectError<CarouselBlock>({ type: 'carousel' }); // missing required elements
// -- happy path
expectAssignable<CarouselBlock>({
type: 'carousel',
elements: [{ type: 'card', title: { type: 'mrkdwn', text: 'Card 1' } }],
});
expectAssignable<CarouselBlock>({
type: 'carousel',
elements: [
{ type: 'card', title: { type: 'mrkdwn', text: 'Card 1' } },
{ type: 'card', body: { type: 'mrkdwn', text: 'Card 2 body' } },
],
});
expectAssignable<KnownBlock>({
type: 'carousel',
elements: [{ type: 'card' }],
});