slack-block-builder
Advanced tools
Comparing version 0.1.4 to 1.0.0
# Change Log | ||
## v1.0.0 – 2020-06-17 | ||
* Add `getBlocks()` method to all `Surface` objects. | ||
* Add doc site at [https://blockbuilder.dev](https://blockbuilder.dev). | ||
## v0.1.4 – 2020-06-14 | ||
@@ -4,0 +9,0 @@ |
{ | ||
"name": "slack-block-builder", | ||
"version": "0.1.4", | ||
"description": "Lightweight, zero-dependency library for creating Slack messages and modals, with a builder interface inspired by SwiftUI.", | ||
"version": "1.0.0", | ||
"description": "Maintainable code for interactive Slack messages, modals and home tabs. A must-have for the Slack Block Kit framework.", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Ray East", |
433
README.md
@@ -13,3 +13,3 @@ <p align="center"> | ||
<br /> | ||
<a href="#mag--real-world-examples"><strong>View Real-World Examples »</strong></a> | ||
<a href="https://blockbuilder.dev" target="_blank"><strong>View the Docs »</strong></a> | ||
<br /> | ||
@@ -40,8 +40,7 @@ <br /> | ||
* Simple [SwiftUI](https://developer.apple.com/xcode/swiftui/)-inspired syntax. | ||
* Builder syntax for better visual code structure. | ||
* Constructor interface that can be used together with the builder interface for maximum flexibility. | ||
* Simple [SwiftUI](https://developer.apple.com/xcode/swiftui/) inspired syntax. | ||
* In-depth [doc site](https://blockbuilder.dev) at [https://blockbuilder.dev](https://blockbuilder.dev). | ||
* Support for all current Slack Block Kit objects – Surfaces, Blocks, Elements, and Composition Objects ([View Support](#object-support-and-reference)). | ||
* Super helpful JSDoc hints that include real-world explanations, Slack validation rules, and a direct link to the object's documentation on [Slack's API doc site](https://api.slack.com/block-kit). | ||
* Output of the composed UI as either an object or JSON. | ||
* Output of the composed UI as either an object, JSON string, or array of blocks. | ||
* A `printPreviewURL()` method that outputs to the console a link to preview your UI on Slack's [Block Kit Builder website](https://app.slack.com/block-kit-builder). | ||
@@ -52,3 +51,2 @@ * Small size, with zero dependencies. | ||
* In-depth doc site. | ||
* TypeScript type definitions. | ||
@@ -85,13 +83,5 @@ * Components, such as an Accordion module. | ||
* [**Importing**](#importing) | ||
* [**Exposed Objects**](#exposed-objects) | ||
* [**Object Support and Reference**](#object-support-and-reference) | ||
* [**Heirarchy/Structure**](#heirarchystructure) | ||
* [**Creating a Simple Interactive Message**](#creating-a-simple-interactive-message) | ||
* [**Creating a Simple Modal**](#creating-a-simple-modal) | ||
* [**About The Builder Methods**](#about-the-builder-methods) | ||
* [**Output**](#output) | ||
* [**Advanced Use Cases**](#advanced-use-cases) | ||
- [**Using Conditionals**](#using-conditionals) | ||
:books: **For full documentation, make sure you head over to [https://blockbuilder.dev](https://blockbuilder.dev).** | ||
### Importing | ||
@@ -107,3 +97,3 @@ | ||
### Exposed Objects | ||
### Exposed Imports | ||
@@ -128,41 +118,27 @@ `Modal` – Used to create a modal surface. | ||
| **Type** | **Name** | **Support** | **Access Via** | ||
|----------------------|--------------------|----------------|---------------- | ||
| Surface | Message | ✅ | `Message()` | ||
| Surface | Modal | ✅ | `Modal()` | ||
| Surface | App Home | ✅ | `AppHome()` | ||
| Block | Actions | ✅ | `Blocks.Actions()` | ||
| Block | Context | ✅ | `Blocks.Context()` | ||
| Block | Divider | ✅ | `Blocks.Divider()` | ||
| Block | File | ✅ | `Blocks.File()` | ||
| Block | Image | ✅ | `Blocks.Image()` | ||
| Block | Input | ✅ | `Blocks.Input()` | ||
| Block | Section | ✅ | `Blocks.Section()` | ||
| Element | Button | ✅️ | `Elements.Button()` | ||
| Element | Checkboxes | ✅ | `Elements.Checkboxes()` | ||
| Element | Date Picker | ✅ | `Elements.DatePicker()` | ||
| Element | Image | ✅ | `Elements.Img()` | ||
| Element | Overflow Menu | ✅ | `Elements.OverflowMenu()` | ||
| Element | Radio Buttons | ✅ | `Elements.RadioButtons()` | ||
| Element | Plain Text Input | ✅ | `Elements.TextInput()` | ||
| Element | Select Menus | ✅ | `Elements.[Type]Select()` | ||
| Element | Multi-Select Menus | ✅ | `Elements.[Type]MultiSelect()` | ||
| Composition Object | Option | ✅ | `Bits.Option()` | ||
| Composition Object | Confirm Dialog | ✅ | `Bits.ConfirmationDialog()` | ||
| Composition Object | Option Group | ✅ | `Bits.OptionGroup()` | ||
| Composition Object | Text Object | ✅ | Built in Background (Both Plain and Markdown) | ||
| Composition Object | Filter Object | ✅ | Built in Background | ||
| **Name** | **Type** | **Support** | **Accessed Via** | ||
|----------------------|--------------------|--------------------------------|----------------------------- | ||
| Home Tab | Surface | :white_check_mark: | `HomeTab()` | ||
| Message | Surface | :white_check_mark: | `Message()` | ||
| Modal | Surface | :white_check_mark: | `Modal()` | ||
| Actions | Block | :white_check_mark: | `Blocks.Actions()` | ||
| Context | Block | :white_check_mark: | `Blocks.Context()` | ||
| Divider | Block | :white_check_mark: | `Blocks.Divider()` | ||
| File | Block | :white_check_mark: | `Blocks.File()` | ||
| Image | Block | :white_check_mark: | `Blocks.Image()` | ||
| Input | Block | :white_check_mark: | `Blocks.Input()` | ||
| Section | Block | :white_check_mark: | `Blocks.Section()` | ||
| Button | Element | :white_check_mark:️ | `Elements.Button()` | ||
| Checkboxes | Element | :white_check_mark: | `Elements.Checkboxes()` | ||
| Date Picker | Element | :white_check_mark: | `Elements.DatePicker()` | ||
| Image | Element | :white_check_mark: | `Elements.Img()` | ||
| Overflow Menu | Element | :white_check_mark: | `Elements.OverflowMenu()` | ||
| Radio Buttons | Element | :white_check_mark: | `Elements.RadioButtons()` | ||
| Plain-Text Input | Element | :white_check_mark: | `Elements.TextInput()` | ||
| Select Menus | Element | :white_check_mark: | `Elements.[Type]Select()` | ||
| Multi-Select Menus | Element | :white_check_mark: | `Elements.[Type]MultiSelect()` | ||
| Option | Composition Object | :white_check_mark: | `Bits.Option()` | ||
| Confirm Dialog | Composition Object | :white_check_mark: | `Bits.ConfirmationDialog()` | ||
| Option Group | Composition Object | :white_check_mark: | `Bits.OptionGroup()` | ||
### Heirarchy/Structure | ||
When building views, there'a certain heirarchy that needs to be followed, which is true to any library that works with [Slack](https://slack.com) Blocks: | ||
_***Surfaces => Contain Blocks***_ – Think of these as the `<body>` HTML tag. | ||
_***Blocks => Contain Elements***_ – Think of these as the `<section>` or `<div>` HTML tags. | ||
_***Elements => Contain Bits***_ – More often than not, `Elements` are various types of input controls. | ||
Bits are small pieces of the UI. Such as an `Option` for a select menu. However, the `ConfirmationDialog` Bit can be present in multiple areas of the UI, not just in `Elements`. | ||
### Creating a Simple Interactive Message | ||
@@ -172,3 +148,3 @@ | ||
We can create a piece of UI using only the builder methods: | ||
We can create a piece of UI using only the setter methods: | ||
@@ -202,3 +178,3 @@ ```javascript | ||
``` | ||
Alternatively (and preferably), we can combine both the builder methods and the constructors to shorten it: | ||
Alternatively (and preferably), we can combine both the setter methods and the params to shorten it: | ||
@@ -231,10 +207,2 @@ ```javascript | ||
If you look up at the example again, you can see there is more going on than just building out UI. | ||
In this function, we are also defining the channel to which the message is sent, and also invoking the `asUser()` method. The `Message` and `Modal` objects in **Block Builder** both include similar methods. | ||
If you've been working with the [Slack API](https://api.slack.com/) for a while, you know about these options already. More on that coming with the full documentation. In the meantime, check out the IDE hints and the rest of this README. | ||
However, you'll definitely want to take a look at the [Output](#output) section to get an idea of the methods used to get the final output. | ||
### Creating a Simple Modal | ||
@@ -244,5 +212,5 @@ | ||
In this part, we'll also take a look at working with Bits. You'll see in this example that we're hardcoding the options in the select menu. There are, of course, better ways to handle that. That will be covered a bit later in the [About The Builder Methods](#about-the-builder-methods) section. | ||
Here we'll also take a look at working with Bits. You'll see in this example that we're hardcoding the options in the select menu. There are, of course, better ways to handle that, by using the `Array.map()` method, but they are listed here separately to demonstrate the usage. | ||
First, an example using just our builder methods: | ||
First, an example using just our setter methods: | ||
@@ -288,3 +256,3 @@ ```javascript | ||
Alternatively (and preferably), we can combine both the builder methods and the constructors to shorten it: | ||
Alternatively (and preferably), we can combine both the setter methods and the params to shorten it: | ||
@@ -326,327 +294,2 @@ ```javascript | ||
Similar to the `Message` object, the `Modal` object also supports non UI-related options, such as `notifyOnClose()` or `clearOnClose()`. But those will be covered in the full documentation. | ||
However, you'll definitely want to take a look at the [Output](#output) section to get an idea of the methods used to get the final output. | ||
The above examples should give you a general idea of how to use **Block Builder**, so we'll skip creating a `HomeTab` object, as it almost identical to building out a [Slack](https://slack.com) modal. | ||
### About The Builder Methods | ||
Using the builder methods is super straightforward, but there are a few things that you should know and consider before starting. | ||
#### All Builder Methods Accept `undefined` | ||
If you pass an argument that is `undefined`, you will not get an error. Instead, the property will simply not be set. And **it's a feature**, not a bug. | ||
To learn more as to why that is, see the [Advanced Use Cases](#advanced-use-cases) section. | ||
#### Some Methods Set, Some Append | ||
Certain builder methods set a single value, whereas others append a new value to an array of existing values. | ||
As a general rule – methods that end in an 's' will append. A few examples are `Modal.blocks()`, `Blocks.Actions.elements()`, and `Elements.StaticSelect.options()`. | ||
And as such... | ||
Methods that *set* can only be called once. | ||
```javascript | ||
const myModal = () => { | ||
return Modal() | ||
.title('My Modal') // Sets the title of the modal | ||
.blocks( /* Imagine Blocks */ ) | ||
.title('My Modal') // Will throw an error, as it has already been set through the 'title()' method | ||
.buildToJSON(); | ||
}; | ||
``` | ||
This also applies to properties that have been set in the constructor. | ||
```javascript | ||
const myOtherModal = () => { | ||
return Modal({ title: 'My Other Modal' }) | ||
.title('My Modal') // Will throw an error, as it has already been set through the constructor | ||
.blocks( /* Imagine blocks */ ) | ||
.buildToJSON(); | ||
}; | ||
``` | ||
Methods that _append_ can be called multiple times. | ||
```javascript | ||
const myModal = () => { | ||
return Modal({ title: 'My Other Modal' }) | ||
.blocks( /* Add some block */ ) | ||
.blocks( /* Add another */ ) // Does not throw error, simply appends the Block object | ||
.buildToJSON(); | ||
}; | ||
``` | ||
#### Methods That Append Accept Arrays and Multiple Args | ||
When using a method that appends a value, or a set of values, you can pass in: | ||
* A single argument. | ||
* Multiple arguments. | ||
* An array. | ||
This way, you can code however you want, withought being forced into a certain paradigm by **Block Builder**. | ||
All three of the following examples produce the exact same result: | ||
```javascript | ||
// Passing a single argument | ||
const myModal = () => { | ||
return Modal({ title: 'Single Arg Example' }) | ||
.blocks( | ||
Blocks.Section({ text: 'This is the first section.' })) | ||
.blocks( | ||
Blocks.Section({ text: 'This is the second section.' })) | ||
.blocks( | ||
Blocks.Section({ text: 'This is the third section.' })) | ||
.buildToJSON(); | ||
}; | ||
``` | ||
```javascript | ||
// Passing multiple arguments | ||
const myModal = () => { | ||
return Modal({ title: 'Multiple Args Example' }) | ||
.blocks( | ||
Blocks.Section({ text: 'This is the first section.' })) | ||
.blocks( | ||
Blocks.Section({ text: 'This is the second section.' }), | ||
Blocks.Section({ text: 'This is the third section.' })) | ||
.buildToJSON(); | ||
}; | ||
``` | ||
```javascript | ||
// Passing an array | ||
const myModal = () => { | ||
return Modal({ title: 'Multiple Args Example' }) | ||
.blocks( | ||
Blocks.Section({ text: 'This is the first section.' })) | ||
.blocks([ | ||
Blocks.Section({ text: 'This is the second section.' }), | ||
Blocks.Section({ text: 'This is the third section.' }), | ||
]) | ||
.buildToJSON(); | ||
}; | ||
``` | ||
This also allows you to use helpful functions such as `Array.map()`, `Array.reduce()`, etc. | ||
Returning to our modal example from above, we can use this to programmatically create the list of options: | ||
```javascript | ||
const myShorterModal = ({ menu }) => { // Pass in an array of menu items from data source | ||
return Modal({ title: 'PizzaMate', submit: 'Get Fed' }) | ||
.blocks( | ||
Blocks.Section({ text: 'Hey there, colleague!' }), | ||
Blocks.Section({ text: 'Hurray for corporate pizza! Let\'s get you fed and happy :pizza:' }), | ||
Blocks.Input({ label: 'What can we call you?' }) | ||
.element( | ||
Elements.TextInput({ placeholder: 'Hi, my name is... (What?!) (Who?!)' }) | ||
.actionId('name')), | ||
Blocks.Input({ label: 'Which floor are you on?' }) | ||
.element( | ||
Elements.TextInput({ placeholder: 'HQ – Fifth Floor' }) | ||
.actionId('floor')), | ||
Blocks.Input({ label: 'What\'ll you have?' }) | ||
.element( | ||
Elements.StaticSelect({ placeholder: 'Choose your favorite...' }) | ||
.actionId('item') | ||
.options(menu.map((item) => Bits.Option({ text: item.name, value: item.id }))))) // Map items to Option objects | ||
.buildToJSON(); | ||
}; | ||
``` | ||
### Output | ||
There are a few different methods that can be called on surfaces (`Message`, `Modal`, `HomeTab`) to get various types of output: | ||
```javascript | ||
Message.buildToJSON() | ||
Modal.buildToJSON() | ||
HomeTab.buildToJSON() | ||
``` | ||
Returns a JSON (string) representation of the UI, compatible with the [Slack API](https://api.slack.com/). | ||
```javascript | ||
Message.buildToObject() | ||
Modal.buildToObject() | ||
HomeTab.buildToObject() | ||
``` | ||
Returns an object representation of the UI, compatible with the [Slack API](https://api.slack.com/) (once stringified). | ||
```javascript | ||
Message.printPreviewUrl() | ||
Modal.printPreviewUrl() | ||
HomeTab.printPreviewUrl() | ||
``` | ||
Logs a preview URL to Slack's [Block Kit Builder website](https://app.slack.com/block-kit-builder) to the console. | ||
```javascript | ||
Message.getBlocks() | ||
``` | ||
Returns an array of `Block` objects attached to the `Message` object. Use this if you wish to move all message configuration to another area of your application. | ||
Note that only one method can be called on the object, and needs to be called after all of the necessary properties have been set. | ||
### Advanced Use Cases | ||
#### Using Conditionals | ||
There are multiple ways to handle conditionals with **Block Builder**. This is where **Block Builder** provides the most flexibility, since the builder methods, when receiving an argument that is `undefined`, simply do not set the property. | ||
While you can definitely use traditional `if` statements, you can also pass in potentially undefined values, inline ternary expressions, the `&&` operator, or even self-invoking functions. Below there is a [real-world example](#mag--real-world-examples), but let's first take a look at the basics: | ||
```javascript | ||
import { Modal, Blocks, Elements, Bits } from 'slack-block-builder'; | ||
const someModal = ({ someData }) => { | ||
return Modal({ title: 'Using If Statements' }) | ||
.blocks( | ||
Blocks.Section({ text: 'Let\'s take a look at conditionals.' }), | ||
Blocks.Section({ text: 'This is a much better way to do it!' })) | ||
.blocks(someData && [ | ||
Blocks.Section({ text: 'This is added if condition is true.' }), | ||
Blocks.Section({ text: 'And it\'s super easy and readable!' }), | ||
]) | ||
.submit(someData ? 'Is Is True' : 'It Is False') | ||
.buildToJSON(); | ||
}; | ||
``` | ||
As you can see, doing this helps keep the view-like structure of the file, while taking full advantage of conditionals. | ||
However, you can definitely use traditional `if` statements: | ||
```javascript | ||
import { Modal, Blocks, Elements, Bits } from 'slack-block-builder'; | ||
const someModal = ({ someData }) => { | ||
const view = Modal({ title: 'Using If Statements' }) | ||
.blocks( | ||
Blocks.Section({ text: 'Let\'s take a look at using an if statement.' }), | ||
Blocks.Section({ text: 'It\'s actually quite simple.' })) | ||
if (someData) { | ||
view.blocks( /* Append Blocks */ ); | ||
} | ||
if (someData) { | ||
view.submit('It Is True'); | ||
} else { | ||
view.submit('It Is False'); | ||
} | ||
return view.buildToJSON(); | ||
}; | ||
``` | ||
When it comes to appending blocks or changing content for a surface, this is perfectly fine. But the code becomes _much less declarative_ once those conditions start to apply to nested objects, as you'll end up moving code to the top of the function: | ||
```javascript | ||
import { Modal, Blocks, Elements, Bits } from 'slack-block-builder'; | ||
const someModal = ({ someData }) => { | ||
const someBlock = Blocks.Section() | ||
someData | ||
? someBlock.text('Can get unintuitive with nested objects.') | ||
: someBlock.text('But there is a much better way!') | ||
const view = Modal({ title: 'Using If Statements' }) | ||
.blocks( | ||
Blocks.Section({ text: 'Let\'s take a look at using an if statement.' }), | ||
someBlock, | ||
) | ||
if (someData) { | ||
view.blocks( /* Append Blocks */ ); | ||
} | ||
if (someData) { | ||
view.submit('It Is True'); | ||
} else { | ||
view.submit('It Is False'); | ||
} | ||
return view.buildToJSON(); | ||
}; | ||
``` | ||
## :mag: Real-World Examples | ||
### Conditionals | ||
Let's say you have an app that manages team rosters, through a [Slack](https://slack.com) modal. | ||
When it opens, it greets the user and presents them with a select menu of teams: | ||
![An example of using Block Builder](resources/images/main/advanced-menu.png) | ||
[**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:%22Edit%20Team%22%7D,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Hey%20there%21%20Let%27s%20make%20sure%20our%20teams%20are%20up%20to%20date%21%22%7D%7D,%7B%22type%22:%22actions%22,%22elements%22:%5B%7B%22type%22:%22static_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Select%20a%20team...%22%7D,%22action_id%22:%22team%22,%22options%22:%5B%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:a:%20%20The%20A-Team%22%7D,%22value%22:%221%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:scales:%20%20The%20Justice%20League%22%7D,%22value%22:%222%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:four:%20%20The%20Fantastic%20Four%22%7D,%22value%22:%223%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:robot_face:%20%20The%20Bot%20Squad%22%7D,%22value%22:%224%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:ribbon:%20%20The%20Pink%20Ladies%22%7D,%22value%22:%225%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:oncoming_automobile:%20%20The%20T-Birds%22%7D,%22value%22:%226%22%7D%5D%7D%5D%7D%5D,%22callback_id%22:%22editTeam%22%7D) | ||
When the user selects a team from the menu, a multiselect is appended to the modal, prepopulated with all of the team's members. In addition, the `Save Changes` button appears: | ||
![An example of using Block Builder](resources/images/main/advanced-menu-with-users.png) | ||
[**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:%22Edit%20Team%22%7D,%22blocks%22:%5B%7B%22type%22:%22section%22,%22text%22:%7B%22type%22:%22mrkdwn%22,%22text%22:%22Hey%20there%21%20Let%27s%20make%20sure%20our%20teams%20are%20up%20to%20date%21%22%7D%7D,%7B%22type%22:%22actions%22,%22elements%22:%5B%7B%22type%22:%22static_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Select%20a%20team...%22%7D,%22action_id%22:%22team%22,%22options%22:%5B%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:a:%20%20The%20A-Team%22%7D,%22value%22:%221%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:scales:%20%20The%20Justice%20League%22%7D,%22value%22:%222%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:four:%20%20The%20Fantastic%20Four%22%7D,%22value%22:%223%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:robot_face:%20%20The%20Bot%20Squad%22%7D,%22value%22:%224%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:ribbon:%20%20The%20Pink%20Ladies%22%7D,%22value%22:%225%22%7D,%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:oncoming_automobile:%20%20The%20T-Birds%22%7D,%22value%22:%226%22%7D%5D,%22initial_option%22:%7B%22text%22:%7B%22type%22:%22plain_text%22,%22text%22:%22:robot_face:%20%20The%20Bot%20Squad%22%7D,%22value%22:%224%22%7D%7D%5D%7D,%7B%22type%22:%22divider%22%7D,%7B%22type%22:%22input%22,%22label%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Team%20Members%22%7D,%22element%22:%7B%22type%22:%22multi_users_select%22,%22placeholder%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Please%20add%20members...%22%7D,%22action_id%22:%22members%22,%22initial_users%22:%5B%22U0142JZHQS1%22,%22U0142K60MJR%22,%22UR7CKBU4W%22,%22USML56H16%22,%22USPPJUL01%22%5D%7D%7D%5D,%22submit%22:%7B%22type%22:%22plain_text%22,%22text%22:%22Save%20Changes%22%7D,%22callback_id%22:%22editTeam%22%7D) | ||
This flow can be called in two different ways: | ||
* Via a slash command `/teams` | ||
* Via a button called `Edit Bot Squad`in an interactive message sent to one of the members. | ||
In the first case, you don't know which team the user wants to edit, so the modal should open with no default option in the select menu, and no appended multiselect menu for the members, either. | ||
However, in the second case, you know exactly which team the user wants to edit, so the modal should open up with the selected team and the team's members. We are counting clicks, right? | ||
Let's try it out with some inline conditionals and see how it reads: | ||
```javascript | ||
import { Modal, Blocks, Elements, Bits } from 'slack-block-builder'; | ||
const editTeamModal = ({ teams, selectedTeamId, members }) => { | ||
return Modal({ title: 'Edit Team' }) | ||
.callbackId('editTeam') | ||
.blocks( | ||
Blocks.Section({ text: 'Hey there! Let\'s make sure our teams are up to date!' }), | ||
Blocks.Input({ label: 'Which team do you want to edit?' }) | ||
.element( | ||
Elements.StaticSelect({ placeholder: 'Select a team...' }) | ||
.actionId('team') | ||
.options(teams.map((team) => Bits.Option({ text: team.name, value: team.id }))) | ||
.initialOption(selectedTeamId && teams | ||
.filter((team) => team.id === selectedTeamId) // Only include initial option is team has been chosen | ||
.map((team) => Bits.Option({ text: team.name, value: team.id }))[0] | ||
))) | ||
.blocks(selectedTeamId && [ // Only include multiselect for members if team has been selected | ||
Blocks.Divider(), | ||
Blocks.Input({ label: 'Team Members' }) | ||
.element( | ||
Elements.UserMultiSelect({ placeholder: 'Please add members...' }) | ||
.actionId('members') | ||
.initialUsers(members && members.map((member) => member.slackId))), // Only include initials members if members are present | ||
]) | ||
.submit(selectedTeamId && 'Save Changes') // Only include 'Save Changes' button if form has actually been loaded | ||
.buildToJSON(); | ||
}; | ||
``` | ||
There are multiple ways to add logic with this in mind. If you're feeling especially adventurous, you could even pass in a self-invoking function with a `switch` block. | ||
Originally, the idea of accepting a value of `undefined` was to help assist with simple things, such as passing in a value to the `initialOption()` method for select menus, but it opens the door for a lot manipulation. It's up to you to decide where you draw the line with this type of inline logic. | ||
## :link: Other Useful Slack-Related Projects | ||
@@ -660,3 +303,3 @@ | ||
<img src="https://github.com/bravecow.png" alt="@bravecow" width="24" height="24" valign="bottom" /> Taras Neporozhniy ([@bravecow](https://github.com/bravecow)) - For all of the advice! | ||
<img src="https://github.com/bravecow.png" alt="@bravecow" width="24" height="24" valign="bottom" /> Taras Neporozhniy ([@bravecow](https://github.com/bravecow)) - For mentorship over the years! | ||
@@ -669,2 +312,2 @@ <img src="https://cdn.dribbble.com/users/683635/avatars/normal/ee2c7c826bfe244b573d145376fe0b5a.png?1510328842" alt="@ft502" width="24" height="24" valign="bottom" /> Alexey Chernyshov ([@ft502](https://dribbble.com/ft502) on Dribbble) - For such a beautiful logo! | ||
<img src="https://github.com/raycharius.png" alt="@raycharius" width="24" height="24" valign="bottom" /> Ray East ([@raycharius](https://github.com/raycharius)) - Huge Fan of Slack and Block Builder Maintainer | ||
<img src="https://github.com/raycharius.png" alt="@raycharius" width="24" height="24" valign="bottom" /> Ray East ([@raycharius](https://github.com/raycharius)) - Huge Fan of Slack and **Block Builder** Maintainer |
@@ -51,4 +51,18 @@ const { Builder } = require('../../utility/lib'); | ||
} | ||
/** | ||
* Builds the view and returns a Slack API-compatible array of Blocks objects. | ||
* | ||
* {@link https://api.slack.com/block-kit|View in Slack API Documentation} | ||
* | ||
* @return {Array} Array of built Block objects | ||
*/ | ||
getBlocks() { | ||
this.build(); | ||
return [...this.result.blocks]; | ||
} | ||
} | ||
module.exports = Surface; |
@@ -151,16 +151,2 @@ const { Surface } = require('./base'); | ||
/** | ||
* Builds the view and returns a Slack API-compatible array of Blocks objects. | ||
* | ||
* {@link https://api.slack.com/messaging/composing|View in Slack API Documentation} | ||
* | ||
* @return {Array} Array of built Block objects | ||
*/ | ||
getBlocks() { | ||
this.build(); | ||
return [...this.result.blocks]; | ||
} | ||
/** | ||
* When called, builds the view and prints to the console the preview URL in | ||
@@ -167,0 +153,0 @@ * order to open and preview the view on the Slack Block Builder website |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
153695
300