From 7999223cc06b6870ab98bfa6ecb21fe1e181167c Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Tue, 4 Oct 2022 17:28:43 -0400 Subject: [PATCH 1/5] Doing a review of block kit types (layout blocks, block elements and composition objects), updated optional/required state on many properties, added missing properties on some interfaces. --- packages/types/src/index.ts | 138 ++++++++++++++++++++++++------------ 1 file changed, 91 insertions(+), 47 deletions(-) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 96cb83584..65e940a6f 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -63,16 +63,10 @@ export interface WorkflowStepView { export type View = HomeView | ModalView | WorkflowStepView; -/* - * Block Elements +/** + * Composition Objects: https://api.slack.com/reference/block-kit/composition-objects */ -export interface ImageElement { - type: 'image'; - image_url: string; - alt_text: string; -} - export interface PlainTextElement { type: 'plain_text'; text: string; @@ -87,55 +81,72 @@ export interface MrkdwnElement { export interface MrkdwnOption { text: MrkdwnElement; - value?: string; + value: string; url?: string; description?: PlainTextElement; } export interface PlainTextOption { text: PlainTextElement; - value?: string; + value: string; url?: string; description?: PlainTextElement; } -export type Option = MrkdwnOption | PlainTextOption; - export interface Confirm { - title?: PlainTextElement; + title: PlainTextElement; text: PlainTextElement | MrkdwnElement; - confirm?: PlainTextElement; - deny?: PlainTextElement; - style?: 'primary' | 'danger'; + confirm: PlainTextElement; + deny: PlainTextElement; + style?: 'primary' | 'danger'; // TODO: factor out into own type +} + +export type Option = MrkdwnOption | PlainTextOption; + +export interface DispatchActionConfig { + trigger_actions_on?: ('on_enter_pressed' | 'on_character_entered')[]; } /* - * Action Types + * Block Elements: https://api.slack.com/reference/block-kit/block-elements + * Also known as interactive elements + * Exported below as Action */ +export type KnownAction = + Select | MultiSelect | Button | Overflow | Datepicker | Timepicker | RadioButtons | Checkboxes | PlainTextInput; + +export interface Action { + type: string; + /** + * @description: A string acting as a unique identifier for a block. + */ + block_id?: string; // TODO: in event payloads, this property will always be present. When defining a block, however, + // it is optional. If not defined when the block is created, Slack will auto-assign a block_id for you - thus why it + // is always present in payloads. So: how can we capture that? Differentiate between the event payload vs. the block + // element? +} + // Selects and Multiselects are available in different surface areas so I've separated them here export type Select = UsersSelect | StaticSelect | ConversationsSelect | ChannelsSelect | ExternalSelect; export type MultiSelect = MultiUsersSelect | MultiStaticSelect | MultiConversationsSelect | MultiChannelsSelect | MultiExternalSelect; -export interface Action { - type: string; - action_id?: string; -} - export interface UsersSelect extends Action { type: 'users_select'; + action_id: string; initial_user?: string; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; confirm?: Confirm; focus_on_load?: boolean; } export interface MultiUsersSelect extends Action { type: 'multi_users_select'; + action_id: string; initial_users?: string[]; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; max_selected_items?: number; confirm?: Confirm; focus_on_load?: boolean; @@ -143,7 +154,8 @@ export interface MultiUsersSelect extends Action { export interface StaticSelect extends Action { type: 'static_select'; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; + action_id: string; initial_option?: PlainTextOption; options?: PlainTextOption[]; option_groups?: { @@ -156,9 +168,10 @@ export interface StaticSelect extends Action { export interface MultiStaticSelect extends Action { type: 'multi_static_select'; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; + action_id: string; initial_options?: PlainTextOption[]; - options?: PlainTextOption[]; + options?: PlainTextOption[]; // TODO: options and option_groups are mutually exclusive option_groups?: { label: PlainTextElement; options: PlainTextOption[]; @@ -171,7 +184,8 @@ export interface MultiStaticSelect extends Action { export interface ConversationsSelect extends Action { type: 'conversations_select'; initial_conversation?: string; - placeholder?: PlainTextElement; + action_id: string; + placeholder: PlainTextElement; confirm?: Confirm; response_url_enabled?: boolean; default_to_current_conversation?: boolean; @@ -186,7 +200,7 @@ export interface ConversationsSelect extends Action { export interface MultiConversationsSelect extends Action { type: 'multi_conversations_select'; initial_conversations?: string[]; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; max_selected_items?: number; confirm?: Confirm; default_to_current_conversation?: boolean; @@ -200,16 +214,19 @@ export interface MultiConversationsSelect extends Action { export interface ChannelsSelect extends Action { type: 'channels_select'; + action_id: string; initial_channel?: string; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; + response_url_enabled?: boolean; confirm?: Confirm; focus_on_load?: boolean; } export interface MultiChannelsSelect extends Action { type: 'multi_channels_select'; + action_id: string; initial_channels?: string[]; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; max_selected_items?: number; confirm?: Confirm; focus_on_load?: boolean; @@ -217,8 +234,9 @@ export interface MultiChannelsSelect extends Action { export interface ExternalSelect extends Action { type: 'external_select'; + action_id: string; initial_option?: PlainTextOption; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; min_query_length?: number; confirm?: Confirm; focus_on_load?: boolean; @@ -226,8 +244,9 @@ export interface ExternalSelect extends Action { export interface MultiExternalSelect extends Action { type: 'multi_external_select'; + action_id: string; initial_options?: PlainTextOption[]; - placeholder?: PlainTextElement; + placeholder: PlainTextElement; min_query_length?: number; max_selected_items?: number; confirm?: Confirm; @@ -235,23 +254,35 @@ export interface MultiExternalSelect extends Action { } export interface Button extends Action { - type: 'button'; + accessibility_label?: string; + /** + * @description: An identifier for this button action. You can use this when you receive an interaction payload to + * identify the source of the action. Should be unique among all other `action_id`s in the containing block. Maximum + * length for this field is 255 characters. + */ + action_id: string; + confirm?: Confirm; + style?: 'danger' | 'primary'; text: PlainTextElement; - value?: string; + type: 'button'; + /** + * @description: A URL to load in the user's browser when the button is clicked. Maximum length for this field is + * 3000 characters. + */ url?: string; - style?: 'danger' | 'primary'; - confirm?: Confirm; - accessibility_label?: string; + value?: string; } export interface Overflow extends Action { type: 'overflow'; + action_id: string; options: PlainTextOption[]; confirm?: Confirm; } export interface Datepicker extends Action { type: 'datepicker'; + action_id: string; initial_date?: string; placeholder?: PlainTextElement; confirm?: Confirm; @@ -260,15 +291,17 @@ export interface Datepicker extends Action { export interface Timepicker extends Action { type: 'timepicker'; + action_id: string; initial_time?: string; placeholder?: PlainTextElement; confirm?: Confirm; focus_on_load?: boolean; - timezone?: string; + timezone?: string; // TODO: this is not listed? https://api.slack.com/reference/block-kit/block-elements#timepicker__fields } export interface RadioButtons extends Action { type: 'radio_buttons'; + action_id: string; initial_option?: Option; options: Option[]; confirm?: Confirm; @@ -277,6 +310,7 @@ export interface RadioButtons extends Action { export interface Checkboxes extends Action { type: 'checkboxes'; + action_id: string; initial_options?: Option[]; options: Option[]; confirm?: Confirm; @@ -285,6 +319,7 @@ export interface Checkboxes extends Action { export interface PlainTextInput extends Action { type: 'plain_text_input'; + action_id: string; placeholder?: PlainTextElement; initial_value?: string; multiline?: boolean; @@ -294,22 +329,30 @@ export interface PlainTextInput extends Action { focus_on_load?: boolean; } -export interface DispatchActionConfig { - trigger_actions_on?: ('on_enter_pressed' | 'on_character_entered')[]; +export interface ImageElement { + type: 'image'; + image_url: string; + alt_text: string; } -/* - * Block Types +/** + * Layout Blocks: https://api.slack.com/reference/block-kit/blocks */ -export type KnownBlock = ImageBlock | ContextBlock | ActionsBlock | DividerBlock | -SectionBlock | InputBlock | FileBlock | HeaderBlock | VideoBlock; - export interface Block { type: string; + /** + * @description: A string acting as a unique identifier for a block. If not specified, a `block_id` will be generated. + * You can use this `block_id` when you receive an interaction payload to identify the source of the action. Maximum + * length for this field is 255 characters. block_id should be unique for each message and each iteration of a + * message. If a message is updated, use a new block_id. + */ block_id?: string; } +export type KnownBlock = ImageBlock | ContextBlock | ActionsBlock | DividerBlock | +SectionBlock | InputBlock | FileBlock | HeaderBlock | VideoBlock; + export interface ImageBlock extends Block { type: 'image'; image_url: string; @@ -324,7 +367,7 @@ export interface ContextBlock extends Block { export interface ActionsBlock extends Block { type: 'actions'; - elements: (Button | Overflow | Datepicker | Timepicker | Select | RadioButtons | Checkboxes | Action)[]; + elements: (Button | Overflow | Datepicker | Timepicker | Select | MultiSelect | RadioButtons | Checkboxes | Action)[]; } export interface DividerBlock extends Block { @@ -339,6 +382,7 @@ export interface SectionBlock extends Block { | Overflow | Datepicker | Timepicker + | PlainTextInput | Select | MultiSelect | Action From f683ec9c63f4055e0cee0270904ef121018c3b3b Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Wed, 5 Oct 2022 12:47:28 -0400 Subject: [PATCH 2/5] All action_ids and placeholders are optional for Block Elements. Factored action_id into top-level Action type. --- packages/types/src/index.ts | 69 +++++++++++++++---------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 65e940a6f..49b27ca2d 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -1,6 +1,7 @@ /* * Reusable shapes for argument values */ +// TODO: dialogs are deprecated https://api.slack.com/dialogs export interface Dialog { title: string; callback_id: string; @@ -88,7 +89,8 @@ export interface MrkdwnOption { export interface PlainTextOption { text: PlainTextElement; - value: string; + value: string; // TODO: technically this property is optional, but if it is not provided, if the option is selected + // as a value in e.g. a static menu, then the returned value will be `null` url?: string; description?: PlainTextElement; } @@ -125,6 +127,7 @@ export interface Action { // it is optional. If not defined when the block is created, Slack will auto-assign a block_id for you - thus why it // is always present in payloads. So: how can we capture that? Differentiate between the event payload vs. the block // element? + action_id?: string; } // Selects and Multiselects are available in different surface areas so I've separated them here @@ -135,18 +138,16 @@ export type MultiSelect = export interface UsersSelect extends Action { type: 'users_select'; - action_id: string; initial_user?: string; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; confirm?: Confirm; focus_on_load?: boolean; } export interface MultiUsersSelect extends Action { type: 'multi_users_select'; - action_id: string; initial_users?: string[]; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; max_selected_items?: number; confirm?: Confirm; focus_on_load?: boolean; @@ -154,13 +155,13 @@ export interface MultiUsersSelect extends Action { export interface StaticSelect extends Action { type: 'static_select'; - placeholder: PlainTextElement; - action_id: string; + placeholder?: PlainTextElement; initial_option?: PlainTextOption; - options?: PlainTextOption[]; - option_groups?: { + options?: PlainTextOption[]; // TODO: mutually exclusive with option_groups but one of them is required + // TODO: minimum length of 1 + option_groups?: { // todo: factor into own interface label: PlainTextElement; - options: PlainTextOption[]; + options: PlainTextOption[]; // TODO: min length 1 }[]; confirm?: Confirm; focus_on_load?: boolean; @@ -168,13 +169,13 @@ export interface StaticSelect extends Action { export interface MultiStaticSelect extends Action { type: 'multi_static_select'; - placeholder: PlainTextElement; - action_id: string; + placeholder?: PlainTextElement; initial_options?: PlainTextOption[]; - options?: PlainTextOption[]; // TODO: options and option_groups are mutually exclusive - option_groups?: { + options?: PlainTextOption[]; // TODO: options and option_groups are mutually exclusive but one of them is required + // TODO: minimum length of 1 + option_groups?: { // todo: factor into own interface label: PlainTextElement; - options: PlainTextOption[]; + options: PlainTextOption[]; // TODO: min length 1 }[]; max_selected_items?: number; confirm?: Confirm; @@ -184,8 +185,7 @@ export interface MultiStaticSelect extends Action { export interface ConversationsSelect extends Action { type: 'conversations_select'; initial_conversation?: string; - action_id: string; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; confirm?: Confirm; response_url_enabled?: boolean; default_to_current_conversation?: boolean; @@ -200,7 +200,7 @@ export interface ConversationsSelect extends Action { export interface MultiConversationsSelect extends Action { type: 'multi_conversations_select'; initial_conversations?: string[]; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; max_selected_items?: number; confirm?: Confirm; default_to_current_conversation?: boolean; @@ -214,9 +214,8 @@ export interface MultiConversationsSelect extends Action { export interface ChannelsSelect extends Action { type: 'channels_select'; - action_id: string; initial_channel?: string; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; response_url_enabled?: boolean; confirm?: Confirm; focus_on_load?: boolean; @@ -224,9 +223,8 @@ export interface ChannelsSelect extends Action { export interface MultiChannelsSelect extends Action { type: 'multi_channels_select'; - action_id: string; initial_channels?: string[]; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; max_selected_items?: number; confirm?: Confirm; focus_on_load?: boolean; @@ -234,9 +232,8 @@ export interface MultiChannelsSelect extends Action { export interface ExternalSelect extends Action { type: 'external_select'; - action_id: string; initial_option?: PlainTextOption; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; min_query_length?: number; confirm?: Confirm; focus_on_load?: boolean; @@ -244,9 +241,9 @@ export interface ExternalSelect extends Action { export interface MultiExternalSelect extends Action { type: 'multi_external_select'; - action_id: string; + // event to return option data. so de-facto this is necessary initial_options?: PlainTextOption[]; - placeholder: PlainTextElement; + placeholder?: PlainTextElement; min_query_length?: number; max_selected_items?: number; confirm?: Confirm; @@ -255,12 +252,6 @@ export interface MultiExternalSelect extends Action { export interface Button extends Action { accessibility_label?: string; - /** - * @description: An identifier for this button action. You can use this when you receive an interaction payload to - * identify the source of the action. Should be unique among all other `action_id`s in the containing block. Maximum - * length for this field is 255 characters. - */ - action_id: string; confirm?: Confirm; style?: 'danger' | 'primary'; text: PlainTextElement; @@ -275,14 +266,12 @@ export interface Button extends Action { export interface Overflow extends Action { type: 'overflow'; - action_id: string; - options: PlainTextOption[]; + options: PlainTextOption[]; // TODO: min length 1 confirm?: Confirm; } export interface Datepicker extends Action { type: 'datepicker'; - action_id: string; initial_date?: string; placeholder?: PlainTextElement; confirm?: Confirm; @@ -291,35 +280,31 @@ export interface Datepicker extends Action { export interface Timepicker extends Action { type: 'timepicker'; - action_id: string; initial_time?: string; placeholder?: PlainTextElement; confirm?: Confirm; focus_on_load?: boolean; - timezone?: string; // TODO: this is not listed? https://api.slack.com/reference/block-kit/block-elements#timepicker__fields + timezone?: string; } export interface RadioButtons extends Action { type: 'radio_buttons'; - action_id: string; initial_option?: Option; - options: Option[]; + options: Option[]; // TODO: min length 1 confirm?: Confirm; focus_on_load?: boolean; } export interface Checkboxes extends Action { type: 'checkboxes'; - action_id: string; initial_options?: Option[]; - options: Option[]; + options: Option[]; // TODO: min length 1 confirm?: Confirm; focus_on_load?: boolean; } export interface PlainTextInput extends Action { type: 'plain_text_input'; - action_id: string; placeholder?: PlainTextElement; initial_value?: string; multiline?: boolean; From faf39050366f47e91ed0808a49579435a94bcd88 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Wed, 5 Oct 2022 14:51:40 -0400 Subject: [PATCH 3/5] Update packages/types/src/index.ts Co-authored-by: Sarah Jiang --- packages/types/src/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 49b27ca2d..615220a22 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -2,6 +2,7 @@ * Reusable shapes for argument values */ // TODO: dialogs are deprecated https://api.slack.com/dialogs +// Guide on upgrading dialogs to modals: https://api.slack.com/block-kit/dialogs-to-modals export interface Dialog { title: string; callback_id: string; From be4cf9c9868b5f941776930d694d1ded029832a9 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Wed, 5 Oct 2022 14:53:48 -0400 Subject: [PATCH 4/5] typo --- packages/types/src/index.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 615220a22..9b2c00c0f 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -242,7 +242,6 @@ export interface ExternalSelect extends Action { export interface MultiExternalSelect extends Action { type: 'multi_external_select'; - // event to return option data. so de-facto this is necessary initial_options?: PlainTextOption[]; placeholder?: PlainTextElement; min_query_length?: number; From 03e8f863940bb06388d84b72f12da73ba18750bb Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Fri, 7 Oct 2022 09:30:30 -0400 Subject: [PATCH 5/5] 2.8.0-centralized.1 pre-release --- packages/types/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/types/package.json b/packages/types/package.json index 844ca76a4..f6e496ed4 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,6 +1,6 @@ { "name": "@slack/types", - "version": "2.7.0", + "version": "2.8.0-centralized.1", "description": "Shared type definitions for the Node Slack SDK", "author": "Slack Technologies, LLC", "license": "MIT",