From 893b8363924f5abd0a39e2a77c4cf5442b1c8242 Mon Sep 17 00:00:00 2001 From: Fil Maj Date: Fri, 14 Jun 2024 09:45:51 -0400 Subject: [PATCH] types: small refactor in views, jsdoc all view properties, add type tests (#1820) --- packages/types/package.json | 7 ++- packages/types/src/views.ts | 68 ++++++++++++++++++++++------- packages/types/test/views.test-d.ts | 24 ++++++++++ 3 files changed, 82 insertions(+), 17 deletions(-) create mode 100644 packages/types/test/views.test-d.ts diff --git a/packages/types/package.json b/packages/types/package.json index 7f080d477..5783bd970 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -32,7 +32,8 @@ "build": "npm run build:clean && tsc", "build:clean": "shx rm -rf ./dist", "lint": "eslint --ext .ts src", - "test": "npm run lint && npm run build && echo \"Tests are not implemented.\" && exit 0", + "test": "npm run lint && npm run build && npm run test:types", + "test:types": "tsd", "ref-docs:model": "api-extractor run" }, "devDependencies": { @@ -47,6 +48,10 @@ "eslint-plugin-jsdoc": "^46.5.0", "eslint-plugin-node": "^11.1.0", "shx": "^0.3.2", + "tsd": "^0.31.0", "typescript": "^4.1.0" + }, + "tsd": { + "directory": "test" } } diff --git a/packages/types/src/views.ts b/packages/types/src/views.ts index 4f8d8c8ce..4bdbf9123 100644 --- a/packages/types/src/views.ts +++ b/packages/types/src/views.ts @@ -1,40 +1,76 @@ import { AnyBlock } from './block-kit/blocks'; import { PlainTextElement } from './block-kit/composition-objects'; -// Reference: https://api.slack.com/surfaces/app-home#composing -export interface HomeView { - type: 'home'; +interface BaseView { + /** @description An array of {@link AnyBlock} that defines the content of the view. Max of 100 blocks. */ blocks: AnyBlock[]; + /** + * @description String that will be sent to your app in + * {@link https://api.slack.com/reference/interaction-payloads/views#view_submission `view_submission`} and + * {@link https://api.slack.com/reference/interaction-payloads/block-actions `block_actions`} events. + * Maximum length of 3000 characters. + */ private_metadata?: string; + /** + * @description An identifier to recognize interactions and submissions of this particular view. Don't use this to + * store sensitive information (use `private_metadata` instead). Maximum length of 255 characters. + * @see {@link https://api.slack.com/surfaces/modals#interactions Handling and responding to interactions}. + */ callback_id?: string; + /** @description A custom identifier that must be unique for all views on a per-team basis. */ external_id?: string; } +// Reference: https://api.slack.com/surfaces/app-home#composing +export interface HomeView extends BaseView { + /** @description The type of view. Set to `home` for Home tabs. */ + type: 'home'; +} + // Reference: https://api.slack.com/surfaces/modals#composing_views -export interface ModalView { +export interface ModalView extends BaseView { + /** @description The type of view. Set to `modal` for modals. */ type: 'modal'; + /** + * @description The title that appears in the top-left of the modal. Must be a {@link PlainTextElement} with a + * maximum length of 24 characters. + */ title: PlainTextElement; - blocks: AnyBlock[]; + /** + * @description An optional {@link PlainTextElement} that defines the text displayed in the close button at the + * bottom-right of the view. Maximum length of 24 characters. + */ close?: PlainTextElement; + /** + * @description An optional {@link PlainTextElement} that defines the text displayed in the submit button at the + * bottom-right of the view. `submit` is required when an input block is within the `blocks` array. Max length of 24 + * characters. + */ submit?: PlainTextElement; - private_metadata?: string; - callback_id?: string; - clear_on_close?: boolean; // defaults to false - notify_on_close?: boolean; // defaults to false - external_id?: string; + /** + * @description When set to `true`, clicking on the close button will clear all views in a modal and close it. + * Defaults to `false`. + */ + clear_on_close?: boolean; + /** + * @description Indicates whether Slack will send your app a + * {@link https://api.slack.com/reference/interaction-payloads/views#view_closed `view_closed`} event when a user + * clicks the close button. Defaults to `false`. + */ + notify_on_close?: boolean; } /** * {@link https://api.slack.com/legacy/workflows/steps#handle_config_view Configuration modal} for {@link https://api.slack.com/legacy/workflows/steps legacy Workflow Steps from Apps}. * @deprecated Steps from Apps are deprecated and will no longer be executed starting September 12, 2024. For more information, see our {@link https://api.slack.com/changelog/2023-08-workflow-steps-from-apps-step-back deprecation announcement}. */ -export interface WorkflowStepView { +export interface WorkflowStepView extends BaseView { type: 'workflow_step'; - blocks: AnyBlock[]; - private_metadata?: string; - callback_id?: string; - submit_disabled?: boolean; // defaults to false - external_id?: string; + /** + * @description When set to `true`, disables the submit button until the user has completed one or more inputs. + * Defaults to `false`. + */ + submit_disabled?: boolean; } export type View = HomeView | ModalView | WorkflowStepView; diff --git a/packages/types/test/views.test-d.ts b/packages/types/test/views.test-d.ts new file mode 100644 index 000000000..48fcb57d8 --- /dev/null +++ b/packages/types/test/views.test-d.ts @@ -0,0 +1,24 @@ +import { expectAssignable, expectError } from 'tsd'; +import { HomeView, ModalView, PlainTextElement } from '../src/index'; + +const plaintext: PlainTextElement = { type: 'plain_text', text: 'hi' }; + +// HomeView +// -- sad path +expectError({}); // missing type, blocks +expectError({ type: 'home' }); // missing blocks +expectError({ blocks: [] }); // missing type +// -- happy path +expectAssignable({ type: 'home', blocks: [] }); + +// ModalView +// -- sad path +expectError({}); // missing type, blocks, title +expectError({ type: 'modal' }); // missing blocks, title +expectError({ blocks: [] }); // missing type, title +expectError({ title: plaintext }); // missing type, blocks +expectError({ type: 'modal', blocks: [] }); // missing title +expectError({ blocks: [], title: plaintext }); // missing type +expectError({ title: plaintext, type: 'modal' }); // missing blocks +// -- happy path +expectAssignable({ type: 'modal', blocks: [], title: plaintext });