Repository navigation
feat(model): add table Block Kit block #1611
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: chore/block-kit-data-table
Are you sure you want to change the base?
Changes from all commits
038976e
3a52834
cdf0bf7
2f5a86e
7ecb24d
a733b91
2028632
7d894ad
fcd609c
729f537
6c5a933
db4c6c7
9325589
0336acb
872276b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| package com.slack.api.model.block; | ||
|
|
||
| import com.slack.api.model.block.composition.TableCell; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Data; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| /** | ||
| * Displays structured information in a table. | ||
| * | ||
| * @see <a href="https://docs.slack.dev/reference/block-kit/blocks/table-block">Table block</a> | ||
| */ | ||
| @Data | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public class TableBlock implements LayoutBlock { | ||
| public static final String TYPE = "table"; | ||
| private final String type = TYPE; | ||
|
|
||
| /** | ||
| * An array consisting of table rows. Maximum 100 rows. Each row object is an array with a max of 20 table | ||
| * cells. Table cells can have a type of {@code rich_text}, {@code raw_text}, or {@code raw_number}. | ||
| */ | ||
| @Builder.Default | ||
| private List<List<TableCell>> rows = new ArrayList<>(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 question (non-blocking): |
||
|
|
||
| /** | ||
| * An array describing column behavior. If there are fewer items in the {@code column_settings} array than | ||
| * there are columns in the table, then the items in the the {@code column_settings} array will describe the | ||
| * same number of columns in the table as there are in the array itself. Any additional columns will have the | ||
| * default behavior. Maximum 20 items. | ||
| */ | ||
| private List<ColumnSetting> columnSettings; | ||
|
|
||
| private String blockId; | ||
|
|
||
| /** | ||
| * Describes the behavior of a column in a {@link TableBlock}. | ||
| * | ||
| * @see <a href="https://docs.slack.dev/reference/block-kit/blocks/table-block#schema-for-column_settings">Schema for column_settings</a> | ||
| */ | ||
| @Data | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| public static class ColumnSetting { | ||
|
|
||
| /** | ||
| * The alignment for items in this column. Can be {@code left}, {@code center}, or {@code right}. Defaults to | ||
| * {@code left} if not defined. | ||
| */ | ||
| private String align; | ||
|
|
||
| /** | ||
| * Whether the contents of this column should be wrapped or not. Defaults to {@code false} if not defined. | ||
| */ | ||
| private Boolean isWrapped; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
|
|
||
| import com.google.gson.Gson; | ||
| import com.google.gson.JsonParseException; | ||
| import com.google.gson.JsonParser; | ||
| import com.slack.api.model.Message; | ||
| import com.slack.api.model.block.*; | ||
| import com.slack.api.model.block.composition.ActionCellObject; | ||
|
|
@@ -2710,4 +2711,156 @@ public void buildAuthProviderBlock() { | |
| "\"name\":{\"type\":\"plain_text\",\"text\":\"Acme\"}," + | ||
| "\"is_connected\":false,\"auth_url\":\"https://acme.example.com/oauth\"}")); | ||
| } | ||
|
|
||
| @Test | ||
| public void parseTableBlock() { | ||
| // https://docs.slack.dev/reference/block-kit/blocks/table-block | ||
| String json = "{\n" + | ||
| " \"blocks\": [\n" + | ||
| " {\n" + | ||
| " \"type\": \"table\",\n" + | ||
| " \"column_settings\": [\n" + | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧪 suggestion (non-blocking): the spec says a |
||
| " {\n" + | ||
| " \"is_wrapped\": true\n" + | ||
| " },\n" + | ||
| " {\n" + | ||
| " \"align\": \"right\"\n" + | ||
| " }\n" + | ||
| " ],\n" + | ||
| " \"rows\": [\n" + | ||
| " [\n" + | ||
| " {\n" + | ||
| " \"type\": \"raw_text\",\n" + | ||
| " \"text\": \"Header A\"\n" + | ||
| " },\n" + | ||
| " {\n" + | ||
| " \"type\": \"raw_text\",\n" + | ||
| " \"text\": \"Header B\"\n" + | ||
| " }\n" + | ||
| " ],\n" + | ||
| " [\n" + | ||
| " {\n" + | ||
| " \"type\": \"raw_text\",\n" + | ||
| " \"text\": \"Data 1A\"\n" + | ||
| " },\n" + | ||
| " {\n" + | ||
| " \"type\": \"rich_text\",\n" + | ||
| " \"elements\": [\n" + | ||
| " {\n" + | ||
| " \"type\": \"rich_text_section\",\n" + | ||
| " \"elements\": [\n" + | ||
| " {\n" + | ||
| " \"text\": \"Data 1B\",\n" + | ||
| " \"type\": \"link\",\n" + | ||
| " \"url\": \"https://slack.com\"\n" + | ||
| " }\n" + | ||
| " ]\n" + | ||
| " }\n" + | ||
| " ]\n" + | ||
| " }\n" + | ||
| " ],\n" + | ||
| " [\n" + | ||
| " {\n" + | ||
| " \"type\": \"raw_text\",\n" + | ||
| " \"text\": \"Data 2A\"\n" + | ||
| " },\n" + | ||
| " {\n" + | ||
| " \"type\": \"rich_text\",\n" + | ||
| " \"elements\": [\n" + | ||
| " {\n" + | ||
| " \"type\": \"rich_text_section\",\n" + | ||
| " \"elements\": [\n" + | ||
| " {\n" + | ||
| " \"text\": \"Data 2B\",\n" + | ||
| " \"type\": \"link\",\n" + | ||
| " \"url\": \"https://slack.com\"\n" + | ||
| " }\n" + | ||
| " ]\n" + | ||
| " }\n" + | ||
| " ]\n" + | ||
| " }\n" + | ||
| " ],\n" + | ||
| " [\n" + | ||
| " {\n" + | ||
| " \"type\": \"raw_text\",\n" + | ||
| " \"text\": \"Data 3A\"\n" + | ||
| " },\n" + | ||
| " {\n" + | ||
| " \"type\": \"raw_number\",\n" + | ||
| " \"value\": 400.5,\n" + | ||
| " \"text\": \"$400.50\"\n" + | ||
| " }\n" + | ||
| " ]\n" + | ||
| " ]\n" + | ||
| " }\n" + | ||
| " ]\n" + | ||
| "}"; | ||
| Gson gson = GsonFactory.createSnakeCase(); | ||
| Message message = gson.fromJson(json, Message.class); | ||
| assertThat(message, is(notNullValue())); | ||
| assertThat(message.getBlocks().size(), is(1)); | ||
|
|
||
| TableBlock block = (TableBlock) message.getBlocks().get(0); | ||
| assertThat(block.getType(), is("table")); | ||
| assertThat(block.getColumnSettings().size(), is(2)); | ||
| assertThat(block.getColumnSettings().get(0).getAlign(), is(nullValue())); | ||
| assertThat(block.getColumnSettings().get(0).getIsWrapped(), is(true)); | ||
| assertThat(block.getColumnSettings().get(1).getAlign(), is("right")); | ||
| assertThat(block.getColumnSettings().get(1).getIsWrapped(), is(nullValue())); | ||
| assertThat(block.getRows().size(), is(4)); | ||
|
|
||
| RawTextObject header = (RawTextObject) block.getRows().get(0).get(0); | ||
| assertThat(header.getType(), is("raw_text")); | ||
| assertThat(header.getText(), is("Header A")); | ||
|
|
||
| RichTextBlock richCell = (RichTextBlock) block.getRows().get(1).get(1); | ||
| assertThat(richCell.getType(), is("rich_text")); | ||
| RichTextSectionElement section = (RichTextSectionElement) richCell.getElements().get(0); | ||
| RichTextSectionElement.Link link = (RichTextSectionElement.Link) section.getElements().get(0); | ||
| assertThat(link.getText(), is("Data 1B")); | ||
| assertThat(link.getUrl(), is("https://slack.com")); | ||
|
|
||
| RawNumberObject numberCell = (RawNumberObject) block.getRows().get(3).get(1); | ||
| assertThat(numberCell.getType(), is("raw_number")); | ||
| assertThat(numberCell.getValue(), is(400.5)); | ||
| assertThat(numberCell.getText(), is("$400.50")); | ||
|
|
||
| assertThat( | ||
| gson.toJsonTree(message.getBlocks()), | ||
| is(JsonParser.parseString(json).getAsJsonObject().get("blocks"))); | ||
| } | ||
|
|
||
| @Test | ||
| public void buildTableBlock() { | ||
| TableBlock block = table(t -> t | ||
| .blockId("bid") | ||
| .columnSettings(Arrays.asList( | ||
| TableBlock.ColumnSetting.builder().isWrapped(true).build(), | ||
| TableBlock.ColumnSetting.builder().align("right").build() | ||
| )) | ||
|
Comment on lines
+2837
to
+2840
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧪 note: Instead of a separate class we might prefer to nest the column settings of a table within the |
||
| .rows(Arrays.asList( | ||
| Arrays.asList( | ||
| RawTextObject.builder().text("Item").build(), | ||
| RawTextObject.builder().text("Cost").build() | ||
| ), | ||
| Arrays.asList( | ||
| RawTextObject.builder().text("Rent").build(), | ||
| RawNumberObject.builder().value(400.5).text("$400.50").build() | ||
| ) | ||
| ))); | ||
| assertThat(block, is(notNullValue())); | ||
|
|
||
| Gson gson = GsonFactory.createSnakeCase(); | ||
| String json = gson.toJson(block); | ||
| assertThat(json, is("{\"type\":\"table\"," + | ||
| "\"rows\":[" + | ||
| "[{\"type\":\"raw_text\",\"text\":\"Item\"},{\"type\":\"raw_text\",\"text\":\"Cost\"}]," + | ||
| "[{\"type\":\"raw_text\",\"text\":\"Rent\"},{\"type\":\"raw_number\",\"value\":400.5,\"text\":\"$400.50\"}]" + | ||
| "]," + | ||
| "\"column_settings\":[{\"is_wrapped\":true},{\"align\":\"right\"}]," + | ||
| "\"block_id\":\"bid\"}")); | ||
|
|
||
| TableBlock restored = (TableBlock) gson.fromJson(json, LayoutBlock.class); | ||
| assertThat(restored, is(block)); | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🪓 suggestion (non-blocking): I think we might not need the field Javadoc here and on
ColumnSetting. It restates the docs page (limits, defaults, even its doubled "the the" below), which the class@seealready links, so it can drift. Same idea for the// TableBlockmarker inBlocks.javaand the docs URL comment in the test.