{"id":13528280,"url":"https://github.com/raycharius/slack-block-builder","last_synced_at":"2025-05-15T12:05:46.016Z","repository":{"id":39984268,"uuid":"271008250","full_name":"raycharius/slack-block-builder","owner":"raycharius","description":"Lightweight Node.js library for building Slack Block Kit UIs, with a declarative syntax inspired by SwiftUI.","archived":false,"fork":false,"pushed_at":"2024-08-18T08:05:17.000Z","size":3867,"stargazers_count":587,"open_issues_count":16,"forks_count":48,"subscribers_count":6,"default_branch":"main","last_synced_at":"2025-04-14T22:19:16.552Z","etag":null,"topics":["bot-application","bot-framework","botkit","bots","slack","slack-api","slack-app","slack-block-kit","slack-bot","slack-bots","slack-commands","slack-dialogs","slack-hacks","slack-interactions","slack-webhook","slackapi","slackbot","slackware","swiftui"],"latest_commit_sha":null,"homepage":"https://blockbuilder.dev","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/raycharius.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":"docs/support.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["raycharius"]}},"created_at":"2020-06-09T13:20:25.000Z","updated_at":"2025-04-12T17:16:48.000Z","dependencies_parsed_at":"2024-01-03T04:56:09.911Z","dependency_job_id":"df66d4b5-4560-41d8-95c5-7c9c0cb4e30b","html_url":"https://github.com/raycharius/slack-block-builder","commit_stats":{"total_commits":351,"total_committers":15,"mean_commits":23.4,"dds":0.05982905982905984,"last_synced_commit":"bdb89d73c6a24a2dae16b7c69f8c6d75da766370"},"previous_names":[],"tags_count":35,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycharius%2Fslack-block-builder","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycharius%2Fslack-block-builder/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycharius%2Fslack-block-builder/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/raycharius%2Fslack-block-builder/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/raycharius","download_url":"https://codeload.github.com/raycharius/slack-block-builder/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254337613,"owners_count":22054253,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bot-application","bot-framework","botkit","bots","slack","slack-api","slack-app","slack-block-kit","slack-bot","slack-bots","slack-commands","slack-dialogs","slack-hacks","slack-interactions","slack-webhook","slackapi","slackbot","slackware","swiftui"],"created_at":"2024-08-01T06:02:23.378Z","updated_at":"2025-05-15T12:05:40.997Z","avatar_url":"https://github.com/raycharius.png","language":"TypeScript","readme":":information_source: **Block Builder** was built in Kyiv, Ukraine :ukraine: If it has saved you time as a developer and brought value to your projects and products, we would like to ask you to consider donating to **[Come Back Alive](https://savelife.in.ua/en/donate/)** to help Ukraine in its fight against Russian aggression. Every cent helps. :pray:\n\n***      \n\n\u003cp align=\"center\"\u003e\n    \u003cimg src=\"https://raw.githubusercontent.com/raycharius/slack-block-builder/master/docs/resources/images/logo-horizontal.png\" alt=\"Logo\" width=\"600px\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    \u003ch3 align=\"center\"\u003eMaintainable code for Slack interactive messages, modals, and home tabs.\u003c/h3\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n    Lightweight, zero-dependency library for declaratively building Slack Block Kit UI.\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"https://blockbuilder.dev\" target=\"_blank\"\u003e\u003cstrong\u003eView the Docs »\u003c/strong\u003e\u003c/a\u003e\n    \u003cbr /\u003e\n    \u003cbr /\u003e\n    \u003ca href=\"#space_invader--usage\"\u003eQuick Start Guide\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/raycharius/slack-block-builder/issues\"\u003eRequest Feature\u003c/a\u003e\n    ·\n    \u003ca href=\"https://github.com/raycharius/slack-block-builder/issues\"\u003eReport Bug\u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\n![An example of using Block Builder](docs/resources/images/block-builder-hero-image.png)\n\n***\n\n[![npm](https://img.shields.io/npm/v/slack-block-builder?color=bright-green)](https://www.npmjs.com/package/slack-block-builder)\n![NPM](https://img.shields.io/npm/l/slack-block-builder?color=bright-green)\n[![codecov](https://codecov.io/gh/raycharius/slack-block-builder/branch/master/graph/badge.svg)](https://codecov.io/gh/raycharius/slack-block-builder)\n\n**Block Builder** helps you keep your Slack app code for UI maintainable, testable, and reusable. It has a declarative, chainable syntax inspired by [SwiftUI](https://developer.apple.com/documentation/swiftui) and is built for better UI architecture.\n\n### :zap: \u0026nbsp; Features\n\n* Declarative [SwiftUI](https://developer.apple.com/xcode/swiftui/) inspired syntax.\n* Commonly-used UI components, such as a `Paginator` and `Accordion`.\n* Inline conditional helper functions for declaratively appending or omitting UI content.\n* The ability to build more complex flows using loops and conditionals.\n* A `printPreviewURL()` method that outputs a link to preview your UI on Slack's [Block Kit Builder website](https://app.slack.com/block-kit-builder) for easier prototyping.\n* A set of helper functions for formatting text with Slack's markdown standard.\n* In-depth [doc site](https://blockbuilder.dev) at [https://blockbuilder.dev](https://blockbuilder.dev).\n* [Support](#block-kit-support-and-reference) for all current Slack Block Kit objects.\n* A great TypeScript experience.\n* Extensive JSDoc hints with explanations, validation rules, and quick links to full documentation.\n* Zero dependencies.\n\n### :gift: \u0026nbsp; Benefits\n\n* Write three times less code.\n* Build more sophisticated, elegant flows.\n* Design better UI architecture for your Slack apps.\n* Focus more on code in your IDE than on studying the [Slack API docs](https://api.slack.com/).\n* Easily integrate localizations into your app.\n\n### :busts_in_silhouette: \u0026nbsp; Join The Community\n\nFeedback – love it! Aside from GitHub Issues, there's a Slack channel available in the Slack Community workspace to discuss **Block Builder** – we'll see you there! :raised_hands:\n\n* **Slack Community** – [\\#block-builder](https://slackcommunity.com)\n\n### :floppy_disk: \u0026nbsp; Installation \n\n**Block Builder** requires Node.js 12 or higher, and, when using TypeScript, TypeScript 3.8 or higher.\n\n#### Using NPM: \n\n``` bash\nnpm install --save slack-block-builder\n```\n\n#### Using Yarn: \n\n``` bash\nyarn add slack-block-builder\n```\n\n### :space_invader: \u0026nbsp; Usage\n\nFor full documentation, make sure you head over to [https://blockbuilder.dev](https://blockbuilder.dev).\n\n\n### Importing\n\nThe functions for creating objects can be both imported directly or through an object that groups them by category.\n\n```javascript\n// Importing exposed groups of objects\n\nimport { Surfaces, Blocks, Elements, Bits, Utilities } from 'slack-block-builder';\n\n// Importing objects top-level\n\nimport { Modal, Section, Actions, Button } from 'slack-block-builder';\n```\n\nThe same goes for importing Slack markdown helper functions:\n\n```javascript\n// Importing the Md object\n\nimport { Surfaces, Blocks, Md } from 'slack-block-builder';\n\n// Importing the functions top-level\n\nimport { Modal, Section, bold, link } from 'slack-block-builder';\n```\n\n### Object Breakdown\n\n`Surfaces` – Contains functions for creating modals, messages, home tabs, and workflow steps. \n\n`Blocks` – Layout blocks used to organize the UI.\n\n`Elements` – UI elements that are used to capture user interaction.\n\n`Bits` – These are composition objects and other bits and pieces from Slack's docs. Included are `Attachment`, `Options`, `OptionGroup`, and `ConfirmationDialog`. They felt like they were deserving of their own category.\n\n`Utilities` – A group of utility functions. See [Utility Functions](#utility-functions).\n\n`Md` – Helper functions for formatting text with Slack's markdown. See [Markdown Helpers](#markdown-helpers).\n\n### Block Kit Support and Reference\n\nBelow is a list of supported objects and how to access them in **Block Builder**:\n\n| **Name**           | **Type**           | **Support**                    | **Accessed Via**                      \n|--------------------|--------------------|--------------------------------|-----------------------------------------\n| Home Tab           | Surface            | :white_check_mark:             | `Surfaces.HomeTab()`                       \n| Message            | Surface            | :white_check_mark:             | `Surfaces.Message()`                       \n| Modal              | Surface            | :white_check_mark:             | `Surfaces.Modal()`                         \n| Workflow Step      | Surface            | :white_check_mark:             | `Surfaces.WorkflowStep()`                         \n| Actions            | Block              | :white_check_mark:             | `Blocks.Actions()`                \n| Context            | Block              | :white_check_mark:             | `Blocks.Context()`                \n| Divider            | Block              | :white_check_mark:             | `Blocks.Divider()`                \n| File               | Block              | :white_check_mark:             | `Blocks.File()`\n| Header             | Block              | :white_check_mark:             | `Blocks.Header()`\n| Image              | Block              | :white_check_mark:             | `Blocks.Image()`                  \n| Input              | Block              | :white_check_mark:             | `Blocks.Input()`                  \n| Section            | Block              | :white_check_mark:             | `Blocks.Section()`\n| Video              | Block              | :white_check_mark:             | `Blocks.Video()`                \n| Button             | Element            | :white_check_mark:️             | `Elements.Button()`               \n| Checkboxes         | Element            | :white_check_mark:             | `Elements.Checkboxes()`           \n| Date Picker        | Element            | :white_check_mark:             | `Elements.DatePicker()`\n| Date Time Picker   | Element            | :white_check_mark:             | `Elements.DateTimePicker()`\n| Email Input        | Element            | :white_check_mark:             | `Elements.EmailInput()`\n| File Input         | Element            | :white_check_mark:             | `Elements.FileInput()`\n| Time Picker        | Element            | :white_check_mark:             | `Elements.TimePicker()`           \n| Image              | Element            | :white_check_mark:             | `Elements.Img()`       \n| Number Input       | Element            | :white_check_mark:             | `Elements.NumberInput()`\n| Overflow Menu      | Element            | :white_check_mark:             | `Elements.OverflowMenu()`         \n| Radio Buttons      | Element            | :white_check_mark:             | `Elements.RadioButtons()`         \n| Plain-Text Input   | Element            | :white_check_mark:             | `Elements.TextInput()`            \n| Select Menus       | Element            | :white_check_mark:             | `Elements.[Type]Select()`          \n| Multi-Select Menus | Element            | :white_check_mark:             | `Elements.[Type]MultiSelect()`\n| URL Input          | Element            | :white_check_mark:             | `Elements.NumberInput()`\n| Option             | Composition Object | :white_check_mark:             | `Bits.Option()`                   \n| Confirm Dialog     | Composition Object | :white_check_mark:             | `Bits.ConfirmationDialog()`       \n| Option Group       | Composition Object | :white_check_mark:             | `Bits.OptionGroup()`\n| Attachment         | Legacy Feature     | :white_check_mark:             | `Bits.Attachment()`              \n\n### Creating a Simple Interactive Message\n\nLet's take a look at how to compose an interactive message. Even though [Slack](https://slack.com) now has modals, these have always been the basis for [Slack](https://slack.com) apps.\n\nFunctions that return Block Kit objects have setter methods for all of the properties, but also support parameters that are passed into the constructor for properties with primitive types.\n\n```javascript\nimport { Message, Blocks, Elements } from 'slack-block-builder';\n\nexport default ({ channel, dangerLevel }) =\u003e\n  Message()\n    .channel(channel)\n    .text('Alas, my friend.')\n    .blocks(\n      Blocks.Section()\n        .text('One does not simply walk into Slack and click a button.'),\n      Blocks.Section()\n        .text('At least that\\'s what my friend Slackomir said :crossed_swords:'),\n      Blocks.Divider(),\n      Blocks.Actions()\n        .elements(\n          Elements.Button()\n            .text('Sure One Does')\n            .actionId('gotClicked')\n            .danger(dangerLevel \u003e 42), // Optional argument, defaults to 'true'\n          Elements.Button()\n            .text('One Does Not')\n            .actionId('scaredyCat')\n            .primary()))\n    .asUser()\n    .buildToJSON();\n```\n\nAnd now an example with using both the setter methods and passing parameters into the functions at initiation:\n\n```javascript\nimport { Message, Blocks, Elements } from 'slack-block-builder';\n\nexport default ({ channel, dangerLevel }) =\u003e\n  Message({ channel, text: 'Alas, my friend.' })\n    .blocks(\n      Blocks.Section({ text: 'One does not simply walk into Slack and click a button.' }),\n      Blocks.Section({ text: 'At least that\\'s what my friend Slackomir said :crossed_swords:' }),\n      Blocks.Divider(),\n      Blocks.Actions()\n        .elements(\n          Elements.Button({ text: 'Sure One Does', actionId: 'gotClicked' })\n            .danger(dangerLevel \u003e 42), // Optional argument, defaults to 'true'\n          Elements.Button({ text: 'One Does Not', actionId: 'scaredyCat' })\n            .primary()))\n    .asUser()\n    .buildToJSON();\n```\n\nBoth of these examples render the message below. And the best part? It only took 15 lines of code, as opposed to the 44 lines of JSON generated as a result. \n\n![An example of using Block Builder for Messages](docs/resources/images/simple-message-example.png)\n\n[**View Example on Slack Block Kit Builder Website**](https://app.slack.com/block-kit-builder#%7B%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22One%20does%20not%20simply%20walk%20into%20Slack%20and%20click%20a%20button.%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22At%20least%20that's%20what%20my%20friend%20Slackomir%20said%20:crossed_swords:%22%7D%7D,%7B%22type%22:%22divider%22%7D,%7B%22type%22:%22actions%22,%22elements%22:%5B%7B%22type%22:%22button%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Sure%20One%20Does%22%7D,%22action_id%22:%22gotClicked%22,%22style%22:%22danger%22%7D,%7B%22type%22:%22button%22,%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22One%20Does%20Not%22%7D,%22action_id%22:%22scaredyCat%22,%22style%22:%22primary%22%7D%5D%7D%5D%7D)\n\n### Creating a Simple Modal\n\nLet's take a look at how modals are created. Here we'll also take a look at working with Bits and with loops, by adding options with the `Array.map()` method. \n\n```javascript\nimport { Modal, Blocks, Elements, Bits, setIfTruthy } from 'slack-block-builder';\n\nexport default ({ menuOptions, selected }) =\u003e\n  Modal({ title: 'PizzaMate', submit: 'Get Fed' })\n    .blocks(\n      Blocks.Section({ text: 'Hey there, colleague!' }),\n      Blocks.Section({ text: 'Hurray for corporate pizza! Let\\'s get you fed and happy :pizza:' }),\n      Blocks.Input({ label: 'What can we call you?' })\n        .element(\n          Elements.TextInput({ placeholder: 'Hi, my name is... (What?!) (Who?!)' })\n            .actionId('name')),\n      Blocks.Input({ label: 'Which floor are you on?' })\n        .element(\n          Elements.TextInput({ placeholder: 'HQ – Fifth Floor' })\n            .actionId('floor')),\n      Blocks.Input({ label: 'What\\'ll you have?' })\n        .element(\n          Elements.StaticSelect({ placeholder: 'Choose your favorite...' })\n            .actionId('item')\n            .options(menuOptions\n              .map((item) =\u003e Bits.Option({ text: item.name, value: item.id })))\n            .initialOption(setIfTruthy(selected, Bits.Option({ text: selected.name, value: selected.id })))))\n    .buildToJSON();\n```\n\nBoth of these examples render the modal below. \n\n![An example of using Block Builder for Modals](docs/resources/images/simple-modal-example.png)\n\n[**View Example on Slack Block Kit Builder Website**](https://app.slack.com/block-kit-builder#%7B%22type%22:%22modal%22,%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22PizzaMate%22%7D,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Hey%20there,%20colleague!%22%7D%7D,%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Hurray%20for%20corporate%20pizza!%20Let's%20get%20you%20fed%20and%20happy%20:pizza:%22%7D%7D,%7B%22type%22:%22input%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22What%20can%20we%20call%20you?%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22name%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Hi,%20my%20name%20is...%20(What?!)%20(Who?!)%22%7D%7D%7D,%7B%22type%22:%22input%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Which%20floor%20are%20you%20on?%22%7D,%22element%22:%7B%22type%22:%22plain_text_input%22,%22action_id%22:%22floor%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22HQ%20–%20Fifth%20Floor%22%7D%7D%7D,%7B%22type%22:%22input%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22What'll%20you%20have?%22%7D,%22element%22:%7B%22type%22:%22static_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Choose%20your%20favorite...%22%7D,%22action_id%22:%22item%22,%22options%22:%5B%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:cheese_wedge:%20With%20Cheeze%22%7D,%22value%22:%22012%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:fish:%20With%20Anchovies%22%7D,%22value%22:%22013%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:cookie:%20With%20Scooby%20Snacks%22%7D,%22value%22:%22014%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:beer:%20I%20Prefer%20Steak%20and%20Beer%22%7D,%22value%22:%22015%22%7D%5D%7D%7D%5D,%22submit%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Get%20Fed%22%7D%7D)\n\n### Paginator Component\n\n**Block Builder** provides a `Paginator` component that assists in producing paginated UI. It allows you to dictate the UI to build for each items passed in and provides to the `actionId` all of the data (`page`, `perPage`, `totalPages`, `offset`, `totalItems` ) you need to produce the right page when a user clicks the Next or Previous buttons.\n\nNote that there is a [demo app available](https://github.com/raycharius/slack-block-builder-demo) that demonstrates how to use components.  \n\nThe `Paginator` component supports optional customizations, such as:  \n\n`nextButtonText` – Used to pass in custom text for the Next button, but has a default.\n\n`previousButtonText` – Used to pass in custom text for the Next button, but has a default.\n\n`pageCountText` – Used to pass in custom text for the page count, accepts a function and passes the function an object with `page` and `totalPages` properties.\n\n```javascript\nimport { Modal, Blocks, Elements, Paginator } from 'slack-block-builder';\n\nexport default ({ tasks, totalTasks, page, perPage }) =\u003e Modal({ title: 'Open Tasks' })\n  .blocks(\n    Blocks.Section({ text: 'Hi! :wave: And welcome to the FAQ section! Take a look around and if you don\\'t find what you need, feel free to open an issue on GitHub.' }),\n    Blocks.Section({ text: `You currently have *${totalTasks} open task(s)*:` }),\n    Paginator({\n      perPage,\n      items: tasks,\n      totalItems: totalTasks,\n      page: page || 1,\n      actionId: ({ page, offset }) =\u003e JSON.stringify({ action: 'render-tasks', page, offset }),\n      blocksForEach: ({ item }) =\u003e [\n        Blocks.Divider(),\n        Blocks.Section({ text: `*${item.title}*` })\n          .accessory(\n            Elements.Button({ text: 'View Details' })\n              .actionId('view-details')\n              .value(item.id.toString())),\n        Blocks.Section({ text: `*Due Date:* ${getDate(item.dueDate)}` }),\n      ],\n    }).getBlocks())\n  .close('Done')\n  .buildToJSON();\n``` \n\nThe code above renders the modal below. And be sure to check out the full documentation on the [**Block Builder** doc site](https://www.blockbuilder.dev/#/components/paginator) for more information.\n\n![An example of using Block Builder for Modals](docs/resources/images/paginator-modal-example.png)\n\n[**View Example on Slack Block Kit Builder Website**](https://app.slack.com/block-kit-builder/#%7B%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Open%20Tasks%22%7D,%22blocks%22:%5B%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Hello!%20:wave::skin-tone-4:%20%20Below%20you%20can%20find%20all%20of%20your%20open%20tasks.%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22You%20currently%20have%20*58%20open%20task(s)*:%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Set%20Up%20Webhook%20Monitoring*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22View%20Details%22%7D,%22action_id%22:%22view-details%22,%22value%22:%225%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Due%20Date:*%20%3C!date%5E1633392000%5E%7Bdate%7D%7C2021-10-05%3E%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Refactor%20Payment%20Service*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22View%20Details%22%7D,%22action_id%22:%22view-details%22,%22value%22:%223%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Due%20Date:*%20%3C!date%5E1635292800%5E%7Bdate%7D%7C2021-10-27%3E%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Finalize%20JumboCode%20Documentation*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22View%20Details%22%7D,%22action_id%22:%22view-details%22,%22value%22:%224%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Due%20Date:*%20%3C!date%5E1635465600%5E%7Bdate%7D%7C2021-10-29%3E%22%7D,%22type%22:%22section%22%7D,%7B%22elements%22:%5B%7B%22type%22:%22mrkdwn%22,%22text%22:%22Page%208%20of%2020%22%7D%5D,%22type%22:%22context%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22elements%22:%5B%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Previous%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-tasks%5C%22,%5C%22page%5C%22:7,%5C%22offset%5C%22:18%7D%22,%22type%22:%22button%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Next%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-tasks%5C%22,%5C%22page%5C%22:9,%5C%22offset%5C%22:24%7D%22,%22type%22:%22button%22%7D%5D,%22type%22:%22actions%22%7D%5D,%22close%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Done%22%7D,%22type%22:%22modal%22%7D)\n\n### Accordion Component\n\nUsing the `Accordion` component, you can easily create a customizable accordion for your Slack app. It not only assists in building a suitable UI, but also calculates the next state and gives you access to it in the `actionId` of the buttons in the accordion, so that you can pass that back to your app's backend and use it to render the next state.\n\nNote that there is a [demo app available](https://github.com/raycharius/slack-block-builder-demo) that demonstrates how to use components.\n\nThe `Accordion` component supports optional customizations, such as: \n\n`collapseOnExpand` – Dictates whether or not multiple items can be expanded at once. When set to true, only one item will be expanded at any given time.\n\n`expandButtonText` – Used to pass in custom text for the button that expands an item, but has a default.\n\n`collapseButtonText` – Used to pass in custom text for the button that collapses an expanded item, but has a default.\n\n`isExpandable` – Used to display or not the expand/collapse button for a given item.\n \n```javascript\nimport { Modal, Blocks, Accordion } from 'slack-block-builder';\n\nexport default ({ faqs, expandedItems }) =\u003e Modal({ title: 'FAQ' })\n  .blocks(\n    Blocks.Section({ text: 'Hi! :wave: And welcome to the FAQ section! Take a look around and if you don\\'t find what you need, feel free to open an issue on GitHub.'}),\n    Blocks.Divider(),\n    Accordion({\n      items: faqs,\n      expandedItems: expandedItems || [], // In this case, the value is [1]\n      collapseOnExpand: true,\n      titleText: ({ item }) =\u003e `*${item.question}*`,\n      actionId: ({ expandedItems }) =\u003e JSON.stringify({ action: 'render-faqs', expandedItems }),\n      blocksForExpanded: ({ item }) =\u003e [\n       Blocks.Section({ text: `${item.answer}` }),\n      ],\n    }).getBlocks())\n  .close('Done')\n  .buildToJSON();\n```\n\nThe code above renders the modal below. And be sure to check out the full documentation on the [**Block Builder** doc site](https://www.blockbuilder.dev/#/components/accordion) for more information.\n\n![An example of using Block Builder for Modals](docs/resources/images/accordion-modal-example.png)\n\n[**View Example on Slack Block Kit Builder Website**](https://app.slack.com/block-kit-builder#%7B%22title%22:%7B%22type%22:%22plain_text%22,%22text%22:%22FAQ%22%7D,%22blocks%22:%5B%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Hi!%20:wave::skin-tone-4:%20And%20welcome%20to%20the%20FAQ%20section!%20Take%20a%20look%20around%20and%20if%20you%20don't%20find%20what%20you%20need,%20feel%20free%20to%20open%20an%20issue%20on%20GitHub.%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Why%20should%20I%20adopt%20Block%20Builder?*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22More%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-faqs%5C%22,%5C%22expandedItems%5C%22:%5B0%5D%7D%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*How%20do%20I%20use%20the%20%60Accordion%60%20component?*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Close%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-faqs%5C%22,%5C%22expandedItems%5C%22:%5B%5D%7D%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22It's%20actually%20_really_%20easy!%20All%20you%20have%20to%20do%20is%20pass%20in%20the%20items%20you%20want%20to%20show%20up%20in%20the%20UI,%20a%20function%20that%20captures%20the%20expanded%20items%20in%20the%20%60actionId%60,%20and%20a%20function%20that%20builds%20out%20the%20right%20UI%20when%20an%20item%20is%20expanded.%5Cn%5CnThat's%20it,%20so%20go%20give%20it%20a%20try!%20%20:raised_hands::skin-tone-4:%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*What%20other%20features%20are%20in%20the%20pipeline?*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22More%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-faqs%5C%22,%5C%22expandedItems%5C%22:%5B2%5D%7D%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*Do%20you%20work%20for%20Slack?*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22More%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-faqs%5C%22,%5C%22expandedItems%5C%22:%5B3%5D%7D%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D,%7B%22type%22:%22divider%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22*What%20can%20I%20do%20to%20help?*%22%7D,%22accessory%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22More%22%7D,%22action_id%22:%22%7B%5C%22action%5C%22:%5C%22render-faqs%5C%22,%5C%22expandedItems%5C%22:%5B4%5D%7D%22,%22type%22:%22button%22%7D,%22type%22:%22section%22%7D%5D,%22close%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Done%22%7D,%22type%22:%22modal%22%7D)\n\n### Utility Functions\n\nThe `Utilities` object contains various utility functions for creating UI. Currently, there are two:\n\n`BlockCollection()` – Accepts multiple arguments or an array of blocks and returns them in an array, in their built state.\n\n`AttachmentCollection()` – Accepts multiple arguments or an array of attachments and returns them in an array, in their built state.\n\n`OptionCollection()` – Accepts multiple arguments or an array of options and returns them in an array, in their built state.\n\n`OptionGroupCollection()` – Accepts multiple arguments or an array of option groups and returns them in an array, in their built state.\n\nBoth `BlockCollection()` and `AttachmentCollection()` are useful when you wish to keep surface or view configuration separate from UI representation.\n\nAn example using Slack's `WebClient` from their [SDK for Node.js](https://github.com/slackapi/node-slack-sdk):\n\n```javascript\nimport { BlockCollection, AttachmentCollection, Blocks } from 'slack-block-builder';\nimport { WebClient } from '@slack/web-api';\n\nconst client = new WebClient(process.env.SLACK_TOKEN);\n\nclient.chat.postMessage({\n  channel: 'ABCDEFG',\n  text: 'Hello, my dear, sweet world!',\n  blocks: BlockCollection( /* Pass in blocks */ ),\n  attachments: AttachmentCollection( /* Pass in attachments */ ),\n})\n.then((response) =\u003e console.log(response))\n.catch((error) =\u003e console.log(error));\n```\n\nAnother example where you might find `BlockCollection()` helpful is when [unfurling links in messages](https://api.slack.com/reference/messaging/link-unfurling):\n\n```javascript\nimport { BlockCollection, Blocks } from 'slack-block-builder';\nimport { WebClient } from '@slack/web-api';\n\nconst client = new WebClient(process.env.SLACK_TOKEN);\n\nconst unfurl = ({ channel, ts, url }) =\u003e client.chat.unfurl({\n  channel,\n  ts,\n  unfurls: { [url]: BlockCollection( /* Pass in blocks */ ) },\n})\n.then((response) =\u003e console.log(response))\n.catch((error) =\u003e console.log(error));\n```\n\nBoth `OptionCollection()` and `OptionGroupCollection()` come in handy when returning an array of options or option groups for select menus with external data sources, as seen in [Slack's API docs](https://api.slack.com/reference/block-kit/block-elements#external_multi_select):\n\n```javascript\nreturn { options: OptionCollection( /* Pass in options */ ) };\n\n// Or:\n\nreturn { options: OptionGroupCollection( /* Pass in option groups */ ) };\n```\n\n### Working With Inline Conditionals\n\nThere are a few helper functions available to make it easy to work with inline conditionals within your UI source code.\n\nThey can be imported separately:\n\n```javascript\nimport { setIfTruthy, omitIfTruthy, setIfFalsy, omitIfFalsy } from 'slack-block-builder';\n```\n\nOr as a part of the `conditionals` object:\n\n```javascript\nimport { conditionals } from 'slack-block-builder';\n```\n\nEach function accepts two arguments – the first being a value that is evaluated whether it is either `null`, `undefined`, or `false`, and the second being the value to set or omit:\n\n```javascript\nimport { Modal, Blocks, Elements, Bits, setIfTruthy } from 'slack-block-builder';\n\nexport default ({ groups, selectedGroup, selectedGroupMembers }) =\u003e Modal()\n  .title('Edit Groups')\n  .callbackId('submit-edit-groups')\n  .blocks(\n    Blocks.Section({ text: 'Hello! Need to edit some groups?'}),\n    Blocks.Input({ label: 'Select a group to get started' })\n      .dispatchAction()\n      .element(\n        Elements.StaticSelect({ placeholder: 'Select a group...' })\n          .actionId('selectedGroup')\n          .options(groups\n            .map(({ name, id }) =\u003e Bits.Option({ text: name, value: id })))),\n    setIfTruthy(selectedGroup, [\n      Blocks.Input({ label: 'Current group members' })\n        .element(\n          Elements.UserMultiSelect({ placeholder: 'Select members...' })\n            .actionId('groupMembers')\n            .initialUsers(selectedGroupMembers))\n    ]))\n  .submit(setIfTruthy(selectedGroup, 'Save Changes'))\n  .buildToJSON();\n```\n\nThese functions essentially return either the value passed into as the second argument or `undefined`, depending on the condition. Please note that falsy is defined as `null`, `undefined`, or `false`. To avoid side effects, values such as `0` or `''` are not considered to be falsy.\n\n### Markdown Helpers\n\nOften you'll find that you need to format text in your messages and modals. *Block Builder* has helper functions available to simply that process. They are available both as members of the `Md` object and as top-level imports. You can find the full list of functions on the [Block Builder doc site](https://blockbuilder.dev):\n\n``` javascript\nimport { Message, Blocks, Md } from 'slack-block-builder';\n\nexport default ({ channel, user }) =\u003e {\n  const slashCommands = ['/schedule', '/cancel', '/remind', '/help'];\n\n  return Message({ channel, text: 'Alas, my friend.' })\n    .blocks(\n      Blocks.Section({ text: `:wave: Hi there, ${Md.user(user)}!` }),\n      Blocks.Section({ text: `${Md.italic('Sorry')}, I didn't get that. Why don't you try out some of my slash commands?` }),\n      Blocks.Section({ text: `Here are some of the things that I can do:` }),\n      Blocks.Section()\n        .text(Md.listBullet(slashCommands\n          .map((item) =\u003e Md.codeInline(item)))))\n    .asUser()\n    .buildToObject();\n};\n```\n\n[**View Example on Slack Block Kit Builder Website**](https://app.slack.com/block-kit-builder/#%7B%22blocks%22:%5B%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22:wave:%20Hi%20there,%20%3C@U03N067AL%3E%21%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22_Sorry_,%20I%20didn%27t%20get%20that.%20Why%20don%27t%20you%20try%20out%20some%20of%20my%20slash%20commands?%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Here%20are%20some%20of%20the%20things%20that%20I%20can%20do:%22%7D,%22type%22:%22section%22%7D,%7B%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22%E2%80%A2%20%60/schedule%60%5Cn%E2%80%A2%20%60/cancel%60%5Cn%E2%80%A2%20%60/remind%60%5Cn%E2%80%A2%20%60/help%60%22%7D,%22type%22:%22section%22%7D%5D%7D)\n \n## :link: \u0026nbsp; Other Useful Slack-Related Projects\n\n[Bolt for JavaScript](https://github.com/SlackAPI/bolt) – A simple framework for building [Slack](https://slack.com) apps, developed by [Slack](https://slack.com) themselves.\n\n[Node Slack SDK](https://github.com/slackapi/node-slack-sdk) – A great and powerful SDK for building [Slack](https://slack.com) Apps from the ground up.\n\n## :fire: \u0026nbsp; Acknowledgements\n \n\u003cimg src=\"https://github.com/korywka.png\" alt=\"@korywka\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Taras Neporozhniy ([@korywka](https://github.com/korywka)) - For help and ideas along the way!\n\n\u003cimg src=\"https://cdn.dribbble.com/users/683635/avatars/normal/ee2c7c826bfe244b573d145376fe0b5a.png?1510328842\" alt=\"@ft502\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Alexey Chernyshov ([@ft502](https://dribbble.com/ft502) on Dribbble) - For such a beautiful logo!\n\n\u003cimg src=\"https://github.com/slackhq.png\" alt=\"@slackhq\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e SlackHQ ([@slackhq](https://github.com/slackhq)) - For such a wonderful product and API!\n\n## :black_nib: \u0026nbsp; Author\n\n\u003cimg src=\"https://github.com/raycharius.png\" alt=\"@raycharius\" width=\"24\" height=\"24\" valign=\"bottom\" /\u003e Ray East ([@raycharius](https://github.com/raycharius)) - Huge Fan of Slack and **Block Builder** Maintainer\n","funding_links":["https://github.com/sponsors/raycharius"],"categories":["TypeScript",":hammer_and_wrench: \u0026nbsp; Libraries and SDKs","Packages"],"sub_categories":["JavaScript/TypeScript"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraycharius%2Fslack-block-builder","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fraycharius%2Fslack-block-builder","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fraycharius%2Fslack-block-builder/lists"}