@slack-wrench/blocks
TypeScript icon, indicating that this package has built-in type declarations

3.1.2 • Public • Published

Blocks

Create messages with block kit more functionally and with less recall and repetition. Never need to remember json formats or accidentally type markdown instead of mrkdwn again!

This package helps abstract away some of the specifics, and deduplicate some of the repetitive code needed to create blocks being sent to Slack.

It also automatically handles various slack API limitations on content to ensure blocks built with dynamic content at least don't break when sent to Slack. Learn more about this in the Limits section.

Install

# Yarn
yarn add @slack-wrench/blocks

# npm
npm install @slack-wrench/blocks

Usage

Example block kit builder search channel message

import { App } from '@slack/bolt';
import { Actions, Blocks, Button, Context, DateString, Divider, Markdown, MdSection, User } from '@slack-wrench/blocks';

const app = new App({ /* token, secret */ });

app.message(':wave:', async ({ message, say }) => {
  say({
    blocks: Blocks([
      MdSection(`Hello, ${User(message.user)}! Let me help you find some channels.`, {
        accessory: Button('Search', 'changeSearch'),
      }),
      Divider(),
      MdSection('*Channels*'),
      MdSection('*house-ravenclaw*\nDiscuss Ravenclaw business'),
      Context([
        Markdown(
          `120 members\nLast post: ${DateString(1575643433, 'date_pretty', '1575643433')}`,
        ),
      ]),
      Actions([
        Button(':thumbsup:', 'thumbsUp', {
          value: 'house-ravenclaw',
        }),
        Button(':thumbsdown:', 'thumbsDown', {
          value: 'house-ravenclaw',
        }),
      ]),
  });
});

Before:

const { App } = require('@slack/bolt');

const app = new App({
  /* token, secret */
});

app.message(':wave:', async ({ message, say }) => {
  say({
    blocks: [
      {
        accessory: {
          type: 'button',
          text: { type: 'plain_text', text: 'Search', emoji: true },
          action_id: 'changeSearch',
        },
        text: {
          type: 'mrkdwn',
          text: `Hello, <@${message.user}>Let me help you find some channels.`,
        },
        type: 'section',
      },
      { type: 'divider' },
      { text: { type: 'mrkdwn', text: '*Channels*' }, type: 'section' },
      {
        text: {
          type: 'mrkdwn',
          text: '*house-ravenclaw*\nDiscuss Ravenclaw business',
        },
        type: 'section',
      },
      {
        type: 'context',
        elements: [
          {
            type: 'mrkdwn',
            text:
              '120 members\nLast post: <!date^1575643433^{date_pretty}|1575643433>',
          },
        ],
      },
      {
        type: 'actions',
        elements: [
          {
            type: 'button',
            text: { type: 'plain_text', text: ':thumbsup:', emoji: true },
            action_id: 'thumbsUp',
            value: 'house-ravenclaw',
          },
          {
            type: 'button',
            text: { type: 'plain_text', text: ':thumbsdown:', emoji: true },
            action_id: 'thumbsDown',
            value: 'house-ravenclaw',
          },
        ],
      },
    ],
  });
});

Limits

The Slack API fails completely if all of the various fields don't meet the length requirements outlined in their documentation. This is a lot to keep track of, so this package makes an attempt at sane defaults for cutting off content so that your requests to the Slack APIs won't error, even if you have very long dynamic content.

Depending on the type of the field being limited, different default limit behavior is applied.

Defaults

There are three primary functions applied, depending on the field type: truncate, ellipsis, or disallow.

  • truncate cuts the field at the maximum. Examples:
    • List of checkboxes - maximum 10 via Slack API; if you try to builds blocks with 15 we'll give you back just the first 10)
    • URLs - in many blocks maximum 3000 characters, but if above that we just cut off the end
  • ellipsis happens for most text fields - titles, descriptions, placeholders, etc.
    • title, descriptions, placeholders, etc.
    • automatically processes text elements ({ text: '<string>'}) as well as string fields
  • disallow happens for any field that is typically programmatic and the value is required to stay the same for app functionality
    • block IDs, action IDs, etc.

For example, on an Option composition object, here are the functions applied by default and the limits for the fields.

const optionLimits: LimitOpts = {
  text: [75, ellipsis],
  value: [75, disallow],
  description: [75, ellipsis],
  url: [3000, truncate],
};

So, the text field has a maximum length of 75. If the provided text in building the block is greater than 75, then the text field is 'limited' via the ellipsis function.

// { text: 'text', value: 'value' }
Option('text', 'value');

// { text: '<first 73 characters> …', value: 'value' }
Option('<80 character text>', 'value');

// Throws
Option('text', '<80 character id value>');

Overriding

You can override the applied functions in most blocks by using the LimiterFuncs argument. This involves passing an object mapping of fields (strings) to functions. Provided functions include truncate, ellipsis, disallow, and identity.

For example, if you didn't want it to throw on a value being too long, you could truncate that field instead of disallowing:

// map the `value` field to the truncate function instead of the default disallow
Option('title', dynamicText, undefined, { value: truncate });

Custom Limiter Functions

In the same way that you can override, you can also provide your own custom functions (e.g. parsing URLs and removing query parameters, showing the last 10 instead of the first 10 in an array...).

The limiter function is passed two values - the limit (number) for the field in context and the string or array on which the check is done.

For example, to provide a function that just rendered an error string for text that is too long, you could do this:

Option(dynamicText, 'value', undefined, {
  text: (limit, dynamicText) =>
    `ERR: TOO LONG. LONGER than (${limit}): ${dynamicText.substring(0, 10)}...`,
});

Limiter function signature: <T>(limit: number, value: T) => T. Be sure to return a value that is the same type as the value passed - text element, string, option object array, etc. depending on the field.

Note that this function only gets called when the passed dynamicText is greater than the limit. Also, if you end up returning a value under the limit, the block could break when the API call is made to Slack (since Slack will refuse the request).

Nested Limits

For some blocks, rules are applied to nested blocks in specific fields. For example, the Section block fields field requires that each text object in fields has a maximum text length of 2000.

In cases such as this, you can override the default limiter function by providing a tuple instead of just a value limiter: [valLimiter, eachLimiters], where eachLimiters is another mapping of Limiters. For example, to truncate at 2000 characters instead of ellipsis for fields on a section, you could do:

Section(
  {
    text: PlainText(text),
    fields: [{ text: dynamicText }],
  },
  {
    fields: [truncate, { text: truncate }],
  },
);

Readme

Keywords

none

Package Sidebar

Install

npm i @slack-wrench/blocks

Weekly Downloads

246

Version

3.1.2

License

Apache-2.0

Unpacked Size

515 kB

Total Files

42

Last publish

Collaborators

  • barlock
  • directctrl