From 61f0681a095cb4fe62f1557872f796dda3f56150 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Fri, 26 Jun 2026 16:50:50 -0700 Subject: [PATCH 01/11] feat(block-kit): add data_table layout block Add the new `data_table` Block Kit layout block to the model: DataTableBlock plus its cell model (DataTableCell interface with RawTextDataTableCell, RawNumberDataTableCell, and RichTextBlock for rich_text cells), the GsonDataTableCellFactory deserializer, factory registration, and a `dataTable` builder DSL entry. Ref: https://docs.slack.dev/reference/block-kit/blocks/data-table-block Co-Authored-By: Claude --- .../com/slack/api/util/json/GsonFactory.java | 2 + .../com/slack/api/model/block/Blocks.java | 6 ++ .../slack/api/model/block/DataTableBlock.java | 55 ++++++++++++ .../slack/api/model/block/DataTableCell.java | 20 +++++ .../model/block/RawNumberDataTableCell.java | 25 ++++++ .../api/model/block/RawTextDataTableCell.java | 22 +++++ .../slack/api/model/block/RichTextBlock.java | 2 +- .../util/json/GsonDataTableCellFactory.java | 56 ++++++++++++ .../api/util/json/GsonLayoutBlockFactory.java | 2 + .../api/model/block/BlockKitTest.java | 88 +++++++++++++++++++ .../java/test_locally/unit/GsonFactory.java | 2 + 11 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java create mode 100644 slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java create mode 100644 slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java create mode 100644 slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java create mode 100644 slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java diff --git a/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java b/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java index 4468f2df4..b8bf7ff9d 100644 --- a/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java +++ b/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java @@ -10,6 +10,7 @@ import com.slack.api.model.admin.AppWorkflow; import com.slack.api.model.block.ContextBlockElement; import com.slack.api.model.block.ContextActionsBlockElement; +import com.slack.api.model.block.DataTableCell; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.TextObject; import com.slack.api.model.block.element.BlockElement; @@ -79,6 +80,7 @@ public static void registerTypeAdapters(GsonBuilder builder, boolean failOnUnkno .registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProps)) .registerTypeAdapter(BlockElement.class, new GsonBlockElementFactory(failOnUnknownProps)) .registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProps)) + .registerTypeAdapter(DataTableCell.class, new GsonDataTableCellFactory(failOnUnknownProps)) .registerTypeAdapter(FunctionExecutedEvent.InputValue.class, new GsonFunctionExecutedEventInputValueFactory()) .registerTypeAdapter(Attachment.VideoHtml.class, new GsonMessageAttachmentVideoHtmlFactory(failOnUnknownProps)) .registerTypeAdapter(MessageChangedEvent.PreviousMessage.class, new GsonMessageChangedEventPreviousMessageFactory(failOnUnknownProps)) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java b/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java index fd451d8d2..747fe8d7a 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java @@ -119,6 +119,12 @@ public static SectionBlock section(ModelConfigurator configurator) { + return configurator.configure(DataTableBlock.builder()).build(); + } + // VideoBlock public static VideoBlock video(ModelConfigurator configurator) { return configurator.configure(VideoBlock.builder()).build(); diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java new file mode 100644 index 000000000..b6f5d3880 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java @@ -0,0 +1,55 @@ +package com.slack.api.model.block; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; +import java.util.List; + +/** + * Displays a sortable, paginated grid of tabular data. + * + *

Each row is a list of {@link DataTableCell cells}; a cell may be a + * {@link RawTextDataTableCell} ({@code raw_text}), {@link RawNumberDataTableCell} + * ({@code raw_number}), or a {@link RichTextBlock} ({@code rich_text}). The first + * row is the header row; header cells cannot use {@code rich_text}. A data table + * supports 1-20 columns and up to 100 data rows (101 rows including the header), + * with all rows sharing the same column count.

+ * + * @see Data table block + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class DataTableBlock implements LayoutBlock { + public static final String TYPE = "data_table"; + private final String type = TYPE; + + /** + * The rows of the table. Each row is a list of cells. The first row is the header row. + * Minimum 2 rows (header plus one data row); maximum 101 rows. Every row must contain + * the same number of cells (1-20). + */ + @Builder.Default + private List> rows = new ArrayList<>(); + + /** + * Required. The caption describing the table, used as the caption of the rendered HTML element. + */ + private String caption; + + /** + * The number of rows shown per page. Valid range 1-100; defaults to 5 when omitted. + */ + private Integer pageSize; + + /** + * Zero-based index of the column that identifies each row. Defaults to 0 when omitted. + */ + private Integer rowHeaderColumnIndex; + + private String blockId; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java new file mode 100644 index 000000000..36a7f4ae5 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java @@ -0,0 +1,20 @@ +package com.slack.api.model.block; + +/** + * A single cell within a {@link DataTableBlock} row. A cell can be one of: + * + *
    + *
  • {@link RawTextDataTableCell} ({@code raw_text})
  • + *
  • {@link RawNumberDataTableCell} ({@code raw_number})
  • + *
  • {@link com.slack.api.model.block.RichTextBlock RichTextBlock} ({@code rich_text})
  • + *
+ * + *

Header cells (those in the first row) cannot use the {@code rich_text} type.

+ * + * @see Data table block + */ +public interface DataTableCell { + + String getType(); + +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java new file mode 100644 index 000000000..57ffab1e8 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java @@ -0,0 +1,25 @@ +package com.slack.api.model.block; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * A {@code raw_number} cell within a {@link DataTableBlock}, holding a numeric value. + * The {@code text} field carries the display representation of the value (for example a + * formatted string) and must be at least one character long. Columns made up entirely of + * {@code raw_number} cells are sorted numerically. + * + * @see Data table block + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RawNumberDataTableCell implements DataTableCell { + public static final String TYPE = "raw_number"; + private final String type = TYPE; + private Double value; + private String text; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java new file mode 100644 index 000000000..1b2230565 --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java @@ -0,0 +1,22 @@ +package com.slack.api.model.block; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * A {@code raw_text} cell within a {@link DataTableBlock}, holding unformatted plain text. + * The {@code text} must be at least one character long. + * + * @see Data table block + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class RawTextDataTableCell implements DataTableCell { + public static final String TYPE = "raw_text"; + private final String type = TYPE; + private String text; +} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java index ad204e225..2b905db59 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java @@ -16,7 +16,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class RichTextBlock implements LayoutBlock { +public class RichTextBlock implements LayoutBlock, DataTableCell { public static final String TYPE = "rich_text"; private final String type = TYPE; @Builder.Default diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java new file mode 100644 index 000000000..d45fa9f5e --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java @@ -0,0 +1,56 @@ +package com.slack.api.util.json; + +import com.google.gson.*; +import com.slack.api.model.block.DataTableCell; +import com.slack.api.model.block.RawNumberDataTableCell; +import com.slack.api.model.block.RawTextDataTableCell; +import com.slack.api.model.block.RichTextBlock; + +import java.lang.reflect.Type; + +/** + * Factory for deserializing the cells of a + * {@link com.slack.api.model.block.DataTableBlock data table block}. + * + * @see Data table block + */ +public class GsonDataTableCellFactory implements JsonDeserializer, JsonSerializer { + + private final boolean failOnUnknownProperties; + + public GsonDataTableCellFactory() { + this(false); + } + + public GsonDataTableCellFactory(boolean failOnUnknownProperties) { + this.failOnUnknownProperties = failOnUnknownProperties; + } + + @Override + public DataTableCell deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + final JsonPrimitive prim = (JsonPrimitive) jsonObject.get("type"); + final String typeName = prim.getAsString(); + final Class clazz = getDataTableCellClassInstance(typeName); + return context.deserialize(jsonObject, clazz); + } + + @Override + public JsonElement serialize(DataTableCell src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } + + private Class getDataTableCellClassInstance(String typeName) { + switch (typeName) { + case RawTextDataTableCell.TYPE: + return RawTextDataTableCell.class; + case RawNumberDataTableCell.TYPE: + return RawNumberDataTableCell.class; + case RichTextBlock.TYPE: + return RichTextBlock.class; + default: + throw new JsonParseException("Unknown data table cell type: " + typeName); + } + } +} diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java index 9286e6b51..c22319913 100644 --- a/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java @@ -59,6 +59,8 @@ private Class getLayoutClassInstance(String typeName) { return VideoBlock.class; case RichTextBlock.TYPE: return RichTextBlock.class; + case DataTableBlock.TYPE: + return DataTableBlock.class; case ShareShortcutBlock.TYPE: return ShareShortcutBlock.class; default: diff --git a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java index 1fc12d58d..f9ce217ce 100644 --- a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java +++ b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java @@ -1742,6 +1742,94 @@ public void parseVideoBlocks() { assertEquals("https://www.youtube.com/embed/RRxQQxiM7AA?feature=oembed&autoplay=1", block.getVideoUrl()); } + @Test + public void parseDataTableBlock() { + // https://docs.slack.dev/reference/block-kit/blocks/data-table-block + String json = "{\n" + + " \"blocks\": [\n" + + " {\n" + + " \"type\": \"data_table\",\n" + + " \"block_id\": \"bid\",\n" + + " \"caption\": \"Quarterly results\",\n" + + " \"page_size\": 10,\n" + + " \"row_header_column_index\": 0,\n" + + " \"rows\": [\n" + + " [\n" + + " { \"type\": \"raw_text\", \"text\": \"Team\" },\n" + + " { \"type\": \"raw_text\", \"text\": \"Revenue\" }\n" + + " ],\n" + + " [\n" + + " {\n" + + " \"type\": \"rich_text\",\n" + + " \"elements\": [\n" + + " {\n" + + " \"type\": \"rich_text_section\",\n" + + " \"elements\": [ { \"type\": \"text\", \"text\": \"Platform\" } ]\n" + + " }\n" + + " ]\n" + + " },\n" + + " { \"type\": \"raw_number\", \"value\": 1234.5, \"text\": \"$1,234.50\" }\n" + + " ]\n" + + " ]\n" + + " }\n" + + " ]\n" + + "}"; + Message message = GsonFactory.createSnakeCase().fromJson(json, Message.class); + assertThat(message, is(notNullValue())); + assertThat(message.getBlocks().size(), is(1)); + + DataTableBlock block = (DataTableBlock) message.getBlocks().get(0); + assertThat(block.getType(), is("data_table")); + assertThat(block.getBlockId(), is("bid")); + assertThat(block.getCaption(), is("Quarterly results")); + assertThat(block.getPageSize(), is(10)); + assertThat(block.getRowHeaderColumnIndex(), is(0)); + assertThat(block.getRows().size(), is(2)); + + RawTextDataTableCell header = (RawTextDataTableCell) block.getRows().get(0).get(0); + assertThat(header.getType(), is("raw_text")); + assertThat(header.getText(), is("Team")); + + DataTableCell richCell = block.getRows().get(1).get(0); + assertTrue(richCell instanceof RichTextBlock); + assertThat(richCell.getType(), is("rich_text")); + + RawNumberDataTableCell numberCell = (RawNumberDataTableCell) block.getRows().get(1).get(1); + assertThat(numberCell.getType(), is("raw_number")); + assertThat(numberCell.getValue(), is(1234.5)); + assertThat(numberCell.getText(), is("$1,234.50")); + } + + @Test + public void buildDataTableBlock() { + DataTableBlock block = dataTable(t -> t + .blockId("bid") + .caption("Quarterly results") + .pageSize(10) + .rowHeaderColumnIndex(0) + .rows(Arrays.asList( + Arrays.asList( + RawTextDataTableCell.builder().text("Team").build(), + RawTextDataTableCell.builder().text("Revenue").build() + ), + Arrays.asList( + RawTextDataTableCell.builder().text("Platform").build(), + RawNumberDataTableCell.builder().value(1234.5).text("$1,234.50").build() + ) + ))); + assertThat(block, is(notNullValue())); + + Gson gson = GsonFactory.createSnakeCase(); + String json = gson.toJson(block); + DataTableBlock restored = (DataTableBlock) gson.fromJson(json, LayoutBlock.class); + assertThat(restored.getType(), is("data_table")); + assertThat(restored.getCaption(), is("Quarterly results")); + assertThat(restored.getPageSize(), is(10)); + assertThat(restored.getRows().size(), is(2)); + RawNumberDataTableCell numberCell = (RawNumberDataTableCell) restored.getRows().get(1).get(1); + assertThat(numberCell.getValue(), is(1234.5)); + } + @Test public void parseLinkTriggerMessages() { // https://tools.slack.dev/deno-slack-sdk/ diff --git a/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java b/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java index 0d30bab1c..f114eb8eb 100644 --- a/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java +++ b/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java @@ -7,6 +7,7 @@ import com.slack.api.model.File; import com.slack.api.model.block.ContextBlockElement; import com.slack.api.model.block.ContextActionsBlockElement; +import com.slack.api.model.block.DataTableCell; import com.slack.api.model.block.LayoutBlock; import com.slack.api.model.block.composition.TextObject; import com.slack.api.model.block.element.BlockElement; @@ -39,6 +40,7 @@ public static Gson createSnakeCase(boolean failOnUnknownProperties, boolean unkn .registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProperties)) .registerTypeAdapter(TextObject.class, new GsonTextObjectFactory(failOnUnknownProperties)) .registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProperties)) + .registerTypeAdapter(DataTableCell.class, new GsonDataTableCellFactory(failOnUnknownProperties)) .registerTypeAdapter(FunctionExecutedEvent.InputValue.class, new GsonFunctionExecutedEventInputValueFactory(failOnUnknownProperties)) .registerTypeAdapter(Attachment.VideoHtml.class, From 715e9a28edcb09caf3a89b729b990fba924c7934 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 17:16:38 -0700 Subject: [PATCH 02/11] docs(block-kit): re-sync data_table javadoc to docs + node review Mirror the reviewed node SDK PR (slackapi/node-slack-sdk#2638): - Correct the max row count: the model previously stated up to 100 data rows (101 including header); the reference documents 201 rows (200 data rows plus header). The 100 cap was page_size, not the row maximum. Drop the stale count from the javadoc rather than restate it. - Align each field's javadoc with the data-table-block reference Fields table verbatim (rows, caption, page_size, row_header_column_index). - Trim RawTextDataTableCell / RawNumberDataTableCell to the terse, docs-minimal descriptions (drop the invented sorting/formatting tails); document value and text per the docs. Ref: https://docs.slack.dev/reference/block-kit/blocks/data-table-block Co-Authored-By: Claude --- .../slack/api/model/block/DataTableBlock.java | 20 ++++++------------- .../slack/api/model/block/DataTableCell.java | 2 -- .../model/block/RawNumberDataTableCell.java | 13 ++++++++---- .../api/model/block/RawTextDataTableCell.java | 7 +++++-- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java index b6f5d3880..fd640cb6a 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java @@ -9,14 +9,7 @@ import java.util.List; /** - * Displays a sortable, paginated grid of tabular data. - * - *

Each row is a list of {@link DataTableCell cells}; a cell may be a - * {@link RawTextDataTableCell} ({@code raw_text}), {@link RawNumberDataTableCell} - * ({@code raw_number}), or a {@link RichTextBlock} ({@code rich_text}). The first - * row is the header row; header cells cannot use {@code rich_text}. A data table - * supports 1-20 columns and up to 100 data rows (101 rows including the header), - * with all rows sharing the same column count.

+ * Displays rich tables that support pagination, sorting, filtering, and interactivity. * * @see Data table block */ @@ -29,25 +22,24 @@ public class DataTableBlock implements LayoutBlock { private final String type = TYPE; /** - * The rows of the table. Each row is a list of cells. The first row is the header row. - * Minimum 2 rows (header plus one data row); maximum 101 rows. Every row must contain - * the same number of cells (1-20). + * An array consisting of table rows. */ @Builder.Default private List> rows = new ArrayList<>(); /** - * Required. The caption describing the table, used as the caption of the rendered HTML element. + * A caption for the table; used as the value for the HTML caption element. */ private String caption; /** - * The number of rows shown per page. Valid range 1-100; defaults to 5 when omitted. + * Number of rows per page. Min {@code 1}, Max {@code 100}. Defaults to {@code 5} if omitted. */ private Integer pageSize; /** - * Zero-based index of the column that identifies each row. Defaults to 0 when omitted. + * The 0-based index of the column that uniquely identifies each row (the row header). This column + * is treated as the row's primary identifier for screen readers. Defaults to {@code 0} if omitted. */ private Integer rowHeaderColumnIndex; diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java index 36a7f4ae5..cf9137618 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java @@ -9,8 +9,6 @@ *
  • {@link com.slack.api.model.block.RichTextBlock RichTextBlock} ({@code rich_text})
  • * * - *

    Header cells (those in the first row) cannot use the {@code rich_text} type.

    - * * @see Data table block */ public interface DataTableCell { diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java index 57ffab1e8..1723550b0 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java @@ -6,10 +6,7 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_number} cell within a {@link DataTableBlock}, holding a numeric value. - * The {@code text} field carries the display representation of the value (for example a - * formatted string) and must be at least one character long. Columns made up entirely of - * {@code raw_number} cells are sorted numerically. + * A {@code raw_number} cell within a {@link DataTableBlock}: an object containing a numeric value. * * @see Data table block */ @@ -20,6 +17,14 @@ public class RawNumberDataTableCell implements DataTableCell { public static final String TYPE = "raw_number"; private final String type = TYPE; + + /** + * The numeric value. + */ private Double value; + + /** + * The text used to display the value. The minimum length is 1 character. + */ private String text; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java index 1b2230565..085504807 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java @@ -6,8 +6,7 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_text} cell within a {@link DataTableBlock}, holding unformatted plain text. - * The {@code text} must be at least one character long. + * A {@code raw_text} cell within a {@link DataTableBlock}: an object containing some text. * * @see Data table block */ @@ -18,5 +17,9 @@ public class RawTextDataTableCell implements DataTableCell { public static final String TYPE = "raw_text"; private final String type = TYPE; + + /** + * The text for the block. The minimum length is 1 character. + */ private String text; } From 6d8ffceb4e9153201880b7b730090f0efccc5a0c Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 17:22:34 -0700 Subject: [PATCH 03/11] refactor(block-kit): group dataTable DSL entry with the net-new blocks Blocks.java keeps the recently-added blocks (Alert, Card, Carousel, Container, TaskCard) in a cluster at the end rather than slotting them alphabetically into the original core entries. Move the dataTable(...) builder into that cluster (after container, before taskCard) so a net-new block sits with its peers instead of splitting the old core from the new group. Co-Authored-By: Claude --- .../main/java/com/slack/api/model/block/Blocks.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java b/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java index e2d0e143c..3c94429b8 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/Blocks.java @@ -119,12 +119,6 @@ public static SectionBlock section(ModelConfigurator configurator) { - return configurator.configure(DataTableBlock.builder()).build(); - } - // VideoBlock public static VideoBlock video(ModelConfigurator configurator) { return configurator.configure(VideoBlock.builder()).build(); @@ -159,6 +153,12 @@ public static ContainerBlock container(ModelConfigurator configurator) { + return configurator.configure(DataTableBlock.builder()).build(); + } + // TaskCardBlock public static TaskCardBlock taskCard(ModelConfigurator configurator) { From 43e03d1306544a749ac2b0b0dc16d4e2f2437d47 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 18:15:13 -0700 Subject: [PATCH 04/11] refactor(block-kit): share the TableCell cell model across table + data_table The data_table cells (raw_text / raw_number / rich_text) are the same cell model the table block uses. Rather than ship a parallel DataTableCell family, adopt the shared TableCell interface and its RawTextTableCell / RawNumberTableCell implementations in com.slack.api.model.block.composition, deserialized by a single GsonTableCellFactory: - Remove DataTableCell, RawTextDataTableCell, RawNumberDataTableCell, and GsonDataTableCellFactory. - Add TableCell, RawTextTableCell, RawNumberTableCell (in ...block.composition) and GsonTableCellFactory (in ...util.json). - DataTableBlock.rows is now List>; RichTextBlock implements TableCell; the client and model-test GsonFactory register TableCell -> GsonTableCellFactory. This is the same cell model the sibling table-block PR (#1611) introduces, placed in the same package with identical definitions, so whichever of the two lands second drops the now-duplicate files in a trivial merge. Mirrors how node (@slack/types composition objects) and python (slack_sdk basic_components) also share one cell model across both blocks. Co-Authored-By: Claude --- .../com/slack/api/util/json/GsonFactory.java | 4 +- .../slack/api/model/block/DataTableBlock.java | 3 +- .../slack/api/model/block/DataTableCell.java | 18 ------ .../slack/api/model/block/RichTextBlock.java | 2 +- .../RawNumberTableCell.java} | 16 ++---- .../RawTextTableCell.java} | 11 ++-- .../model/block/composition/TableCell.java | 19 +++++++ .../util/json/GsonDataTableCellFactory.java | 56 ------------------- .../api/util/json/GsonTableCellFactory.java | 56 +++++++++++++++++++ .../api/model/block/BlockKitTest.java | 19 ++++--- .../java/test_locally/unit/GsonFactory.java | 4 +- 11 files changed, 102 insertions(+), 106 deletions(-) delete mode 100644 slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java rename slack-api-model/src/main/java/com/slack/api/model/block/{RawNumberDataTableCell.java => composition/RawNumberTableCell.java} (53%) rename slack-api-model/src/main/java/com/slack/api/model/block/{RawTextDataTableCell.java => composition/RawTextTableCell.java} (59%) create mode 100644 slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java delete mode 100644 slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java create mode 100644 slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java diff --git a/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java b/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java index c99c02d7b..28a68eb0a 100644 --- a/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java +++ b/slack-api-client/src/main/java/com/slack/api/util/json/GsonFactory.java @@ -10,8 +10,8 @@ import com.slack.api.model.admin.AppWorkflow; import com.slack.api.model.block.ContextBlockElement; import com.slack.api.model.block.ContextActionsBlockElement; -import com.slack.api.model.block.DataTableCell; import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.TableCell; import com.slack.api.model.block.composition.TextObject; import com.slack.api.model.block.element.BlockElement; import com.slack.api.model.block.element.RichTextElement; @@ -81,7 +81,7 @@ public static void registerTypeAdapters(GsonBuilder builder, boolean failOnUnkno .registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProps)) .registerTypeAdapter(BlockElement.class, new GsonBlockElementFactory(failOnUnknownProps)) .registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProps)) - .registerTypeAdapter(DataTableCell.class, new GsonDataTableCellFactory(failOnUnknownProps)) + .registerTypeAdapter(TableCell.class, new GsonTableCellFactory(failOnUnknownProps)) .registerTypeAdapter(FunctionExecutedEvent.InputValue.class, new GsonFunctionExecutedEventInputValueFactory()) .registerTypeAdapter(Attachment.VideoHtml.class, new GsonMessageAttachmentVideoHtmlFactory(failOnUnknownProps)) .registerTypeAdapter(MessageChangedEvent.PreviousMessage.class, new GsonMessageChangedEventPreviousMessageFactory(failOnUnknownProps)) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java index fd640cb6a..83f259409 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableBlock.java @@ -1,5 +1,6 @@ package com.slack.api.model.block; +import com.slack.api.model.block.composition.TableCell; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Data; @@ -25,7 +26,7 @@ public class DataTableBlock implements LayoutBlock { * An array consisting of table rows. */ @Builder.Default - private List> rows = new ArrayList<>(); + private List> rows = new ArrayList<>(); /** * A caption for the table; used as the value for the HTML caption element. diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java deleted file mode 100644 index cf9137618..000000000 --- a/slack-api-model/src/main/java/com/slack/api/model/block/DataTableCell.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.slack.api.model.block; - -/** - * A single cell within a {@link DataTableBlock} row. A cell can be one of: - * - *
      - *
    • {@link RawTextDataTableCell} ({@code raw_text})
    • - *
    • {@link RawNumberDataTableCell} ({@code raw_number})
    • - *
    • {@link com.slack.api.model.block.RichTextBlock RichTextBlock} ({@code rich_text})
    • - *
    - * - * @see Data table block - */ -public interface DataTableCell { - - String getType(); - -} diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java index 2b905db59..19bd1bd46 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java @@ -16,7 +16,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class RichTextBlock implements LayoutBlock, DataTableCell { +public class RichTextBlock implements LayoutBlock, com.slack.api.model.block.composition.TableCell { public static final String TYPE = "rich_text"; private final String type = TYPE; @Builder.Default diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberTableCell.java similarity index 53% rename from slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java rename to slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberTableCell.java index 1723550b0..0c3f1c9e0 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RawNumberDataTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberTableCell.java @@ -1,4 +1,4 @@ -package com.slack.api.model.block; +package com.slack.api.model.block.composition; import lombok.AllArgsConstructor; import lombok.Builder; @@ -6,25 +6,19 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_number} cell within a {@link DataTableBlock}: an object containing a numeric value. + * A {@code raw_number} table cell, holding a numeric value. The optional {@code text} + * field carries the display representation of the value (for example a formatted string). * + * @see Table block * @see Data table block */ @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class RawNumberDataTableCell implements DataTableCell { +public class RawNumberTableCell implements TableCell { public static final String TYPE = "raw_number"; private final String type = TYPE; - - /** - * The numeric value. - */ private Double value; - - /** - * The text used to display the value. The minimum length is 1 character. - */ private String text; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextTableCell.java similarity index 59% rename from slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java rename to slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextTableCell.java index 085504807..69e4aa983 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RawTextDataTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextTableCell.java @@ -1,4 +1,4 @@ -package com.slack.api.model.block; +package com.slack.api.model.block.composition; import lombok.AllArgsConstructor; import lombok.Builder; @@ -6,20 +6,17 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_text} cell within a {@link DataTableBlock}: an object containing some text. + * A {@code raw_text} table cell, holding unformatted plain text. * + * @see Table block * @see Data table block */ @Data @Builder @NoArgsConstructor @AllArgsConstructor -public class RawTextDataTableCell implements DataTableCell { +public class RawTextTableCell implements TableCell { public static final String TYPE = "raw_text"; private final String type = TYPE; - - /** - * The text for the block. The minimum length is 1 character. - */ private String text; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java new file mode 100644 index 000000000..ecc884e2d --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java @@ -0,0 +1,19 @@ +package com.slack.api.model.block.composition; + +/** + * A single cell within a table or data table row. A cell can be one of: + * + *
      + *
    • {@link RawTextTableCell} ({@code raw_text})
    • + *
    • {@link RawNumberTableCell} ({@code raw_number})
    • + *
    • {@link com.slack.api.model.block.RichTextBlock RichTextBlock} ({@code rich_text})
    • + *
    + * + * @see Table block + * @see Data table block + */ +public interface TableCell { + + String getType(); + +} diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java deleted file mode 100644 index d45fa9f5e..000000000 --- a/slack-api-model/src/main/java/com/slack/api/util/json/GsonDataTableCellFactory.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.slack.api.util.json; - -import com.google.gson.*; -import com.slack.api.model.block.DataTableCell; -import com.slack.api.model.block.RawNumberDataTableCell; -import com.slack.api.model.block.RawTextDataTableCell; -import com.slack.api.model.block.RichTextBlock; - -import java.lang.reflect.Type; - -/** - * Factory for deserializing the cells of a - * {@link com.slack.api.model.block.DataTableBlock data table block}. - * - * @see Data table block - */ -public class GsonDataTableCellFactory implements JsonDeserializer, JsonSerializer { - - private final boolean failOnUnknownProperties; - - public GsonDataTableCellFactory() { - this(false); - } - - public GsonDataTableCellFactory(boolean failOnUnknownProperties) { - this.failOnUnknownProperties = failOnUnknownProperties; - } - - @Override - public DataTableCell deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - final JsonObject jsonObject = json.getAsJsonObject(); - final JsonPrimitive prim = (JsonPrimitive) jsonObject.get("type"); - final String typeName = prim.getAsString(); - final Class clazz = getDataTableCellClassInstance(typeName); - return context.deserialize(jsonObject, clazz); - } - - @Override - public JsonElement serialize(DataTableCell src, Type typeOfSrc, JsonSerializationContext context) { - return context.serialize(src); - } - - private Class getDataTableCellClassInstance(String typeName) { - switch (typeName) { - case RawTextDataTableCell.TYPE: - return RawTextDataTableCell.class; - case RawNumberDataTableCell.TYPE: - return RawNumberDataTableCell.class; - case RichTextBlock.TYPE: - return RichTextBlock.class; - default: - throw new JsonParseException("Unknown data table cell type: " + typeName); - } - } -} diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java new file mode 100644 index 000000000..5ff2020ed --- /dev/null +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java @@ -0,0 +1,56 @@ +package com.slack.api.util.json; + +import com.google.gson.*; +import com.slack.api.model.block.RichTextBlock; +import com.slack.api.model.block.composition.RawNumberTableCell; +import com.slack.api.model.block.composition.RawTextTableCell; +import com.slack.api.model.block.composition.TableCell; + +import java.lang.reflect.Type; + +/** + * Factory for deserializing the cells of a + * {@link com.slack.api.model.block.DataTableBlock data table block}. + * + * @see Data table block + */ +public class GsonTableCellFactory implements JsonDeserializer, JsonSerializer { + + private final boolean failOnUnknownProperties; + + public GsonTableCellFactory() { + this(false); + } + + public GsonTableCellFactory(boolean failOnUnknownProperties) { + this.failOnUnknownProperties = failOnUnknownProperties; + } + + @Override + public TableCell deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) + throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + final JsonPrimitive prim = (JsonPrimitive) jsonObject.get("type"); + final String typeName = prim.getAsString(); + final Class clazz = getTableCellClassInstance(typeName); + return context.deserialize(jsonObject, clazz); + } + + @Override + public JsonElement serialize(TableCell src, Type typeOfSrc, JsonSerializationContext context) { + return context.serialize(src); + } + + private Class getTableCellClassInstance(String typeName) { + switch (typeName) { + case RawTextTableCell.TYPE: + return RawTextTableCell.class; + case RawNumberTableCell.TYPE: + return RawNumberTableCell.class; + case RichTextBlock.TYPE: + return RichTextBlock.class; + default: + throw new JsonParseException("Unknown table cell type: " + typeName); + } + } +} diff --git a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java index a4aa4cf59..5bb67b7b1 100644 --- a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java +++ b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java @@ -5,6 +5,9 @@ import com.slack.api.model.Message; import com.slack.api.model.block.*; import com.slack.api.model.block.composition.ConfirmationDialogObject; +import com.slack.api.model.block.composition.RawNumberTableCell; +import com.slack.api.model.block.composition.RawTextTableCell; +import com.slack.api.model.block.composition.TableCell; import com.slack.api.model.block.element.*; import com.slack.api.model.view.View; import org.junit.Test; @@ -1786,15 +1789,15 @@ public void parseDataTableBlock() { assertThat(block.getRowHeaderColumnIndex(), is(0)); assertThat(block.getRows().size(), is(2)); - RawTextDataTableCell header = (RawTextDataTableCell) block.getRows().get(0).get(0); + RawTextTableCell header = (RawTextTableCell) block.getRows().get(0).get(0); assertThat(header.getType(), is("raw_text")); assertThat(header.getText(), is("Team")); - DataTableCell richCell = block.getRows().get(1).get(0); + TableCell richCell = block.getRows().get(1).get(0); assertTrue(richCell instanceof RichTextBlock); assertThat(richCell.getType(), is("rich_text")); - RawNumberDataTableCell numberCell = (RawNumberDataTableCell) block.getRows().get(1).get(1); + RawNumberTableCell numberCell = (RawNumberTableCell) block.getRows().get(1).get(1); assertThat(numberCell.getType(), is("raw_number")); assertThat(numberCell.getValue(), is(1234.5)); assertThat(numberCell.getText(), is("$1,234.50")); @@ -1809,12 +1812,12 @@ public void buildDataTableBlock() { .rowHeaderColumnIndex(0) .rows(Arrays.asList( Arrays.asList( - RawTextDataTableCell.builder().text("Team").build(), - RawTextDataTableCell.builder().text("Revenue").build() + RawTextTableCell.builder().text("Team").build(), + RawTextTableCell.builder().text("Revenue").build() ), Arrays.asList( - RawTextDataTableCell.builder().text("Platform").build(), - RawNumberDataTableCell.builder().value(1234.5).text("$1,234.50").build() + RawTextTableCell.builder().text("Platform").build(), + RawNumberTableCell.builder().value(1234.5).text("$1,234.50").build() ) ))); assertThat(block, is(notNullValue())); @@ -1826,7 +1829,7 @@ public void buildDataTableBlock() { assertThat(restored.getCaption(), is("Quarterly results")); assertThat(restored.getPageSize(), is(10)); assertThat(restored.getRows().size(), is(2)); - RawNumberDataTableCell numberCell = (RawNumberDataTableCell) restored.getRows().get(1).get(1); + RawNumberTableCell numberCell = (RawNumberTableCell) restored.getRows().get(1).get(1); assertThat(numberCell.getValue(), is(1234.5)); } diff --git a/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java b/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java index f114eb8eb..cf45e95e4 100644 --- a/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java +++ b/slack-api-model/src/test/java/test_locally/unit/GsonFactory.java @@ -7,8 +7,8 @@ import com.slack.api.model.File; import com.slack.api.model.block.ContextBlockElement; import com.slack.api.model.block.ContextActionsBlockElement; -import com.slack.api.model.block.DataTableCell; import com.slack.api.model.block.LayoutBlock; +import com.slack.api.model.block.composition.TableCell; import com.slack.api.model.block.composition.TextObject; import com.slack.api.model.block.element.BlockElement; import com.slack.api.model.block.element.RichTextElement; @@ -40,7 +40,7 @@ public static Gson createSnakeCase(boolean failOnUnknownProperties, boolean unkn .registerTypeAdapter(ContextActionsBlockElement.class, new GsonContextActionsBlockElementFactory(failOnUnknownProperties)) .registerTypeAdapter(TextObject.class, new GsonTextObjectFactory(failOnUnknownProperties)) .registerTypeAdapter(RichTextElement.class, new GsonRichTextElementFactory(failOnUnknownProperties)) - .registerTypeAdapter(DataTableCell.class, new GsonDataTableCellFactory(failOnUnknownProperties)) + .registerTypeAdapter(TableCell.class, new GsonTableCellFactory(failOnUnknownProperties)) .registerTypeAdapter(FunctionExecutedEvent.InputValue.class, new GsonFunctionExecutedEventInputValueFactory(failOnUnknownProperties)) .registerTypeAdapter(Attachment.VideoHtml.class, From 698d88ad56cd61676b12156b27b47b76de225444 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 20:12:12 -0700 Subject: [PATCH 05/11] refactor(block-kit): name raw cell classes RawTextObject / RawNumberObject Rename the concrete TableCell implementations from RawTextTableCell / RawNumberTableCell to RawTextObject / RawNumberObject, matching the composition-object naming used by the other SDKs (python RawTextObject / RawNumberObject) and this repo's own ...Object grammar (PlainTextObject, MarkdownTextObject, OptionObject). The shared TableCell interface is the supertype the list is parameterized on (Java has no union types, so List> is how the raw_text | raw_number | rich_text union is expressed); only the implementation names change. Co-Authored-By: Claude --- ...mberTableCell.java => RawNumberObject.java} | 2 +- ...awTextTableCell.java => RawTextObject.java} | 2 +- .../api/model/block/composition/TableCell.java | 4 ++-- .../api/util/json/GsonTableCellFactory.java | 12 ++++++------ .../api/model/block/BlockKitTest.java | 18 +++++++++--------- 5 files changed, 19 insertions(+), 19 deletions(-) rename slack-api-model/src/main/java/com/slack/api/model/block/composition/{RawNumberTableCell.java => RawNumberObject.java} (93%) rename slack-api-model/src/main/java/com/slack/api/model/block/composition/{RawTextTableCell.java => RawTextObject.java} (91%) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java similarity index 93% rename from slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberTableCell.java rename to slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java index 0c3f1c9e0..460efa6c6 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java @@ -16,7 +16,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class RawNumberTableCell implements TableCell { +public class RawNumberObject implements TableCell { public static final String TYPE = "raw_number"; private final String type = TYPE; private Double value; diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextTableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java similarity index 91% rename from slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextTableCell.java rename to slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java index 69e4aa983..46a710b25 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextTableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java @@ -15,7 +15,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class RawTextTableCell implements TableCell { +public class RawTextObject implements TableCell { public static final String TYPE = "raw_text"; private final String type = TYPE; private String text; diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java index ecc884e2d..d83765f5f 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/TableCell.java @@ -4,8 +4,8 @@ * A single cell within a table or data table row. A cell can be one of: * *
      - *
    • {@link RawTextTableCell} ({@code raw_text})
    • - *
    • {@link RawNumberTableCell} ({@code raw_number})
    • + *
    • {@link RawTextObject} ({@code raw_text})
    • + *
    • {@link RawNumberObject} ({@code raw_number})
    • *
    • {@link com.slack.api.model.block.RichTextBlock RichTextBlock} ({@code rich_text})
    • *
    * diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java index 5ff2020ed..d2b01ff7f 100644 --- a/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonTableCellFactory.java @@ -2,8 +2,8 @@ import com.google.gson.*; import com.slack.api.model.block.RichTextBlock; -import com.slack.api.model.block.composition.RawNumberTableCell; -import com.slack.api.model.block.composition.RawTextTableCell; +import com.slack.api.model.block.composition.RawNumberObject; +import com.slack.api.model.block.composition.RawTextObject; import com.slack.api.model.block.composition.TableCell; import java.lang.reflect.Type; @@ -43,10 +43,10 @@ public JsonElement serialize(TableCell src, Type typeOfSrc, JsonSerializationCon private Class getTableCellClassInstance(String typeName) { switch (typeName) { - case RawTextTableCell.TYPE: - return RawTextTableCell.class; - case RawNumberTableCell.TYPE: - return RawNumberTableCell.class; + case RawTextObject.TYPE: + return RawTextObject.class; + case RawNumberObject.TYPE: + return RawNumberObject.class; case RichTextBlock.TYPE: return RichTextBlock.class; default: diff --git a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java index 5bb67b7b1..4eeada10f 100644 --- a/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java +++ b/slack-api-model/src/test/java/test_locally/api/model/block/BlockKitTest.java @@ -5,8 +5,8 @@ import com.slack.api.model.Message; import com.slack.api.model.block.*; import com.slack.api.model.block.composition.ConfirmationDialogObject; -import com.slack.api.model.block.composition.RawNumberTableCell; -import com.slack.api.model.block.composition.RawTextTableCell; +import com.slack.api.model.block.composition.RawNumberObject; +import com.slack.api.model.block.composition.RawTextObject; import com.slack.api.model.block.composition.TableCell; import com.slack.api.model.block.element.*; import com.slack.api.model.view.View; @@ -1789,7 +1789,7 @@ public void parseDataTableBlock() { assertThat(block.getRowHeaderColumnIndex(), is(0)); assertThat(block.getRows().size(), is(2)); - RawTextTableCell header = (RawTextTableCell) block.getRows().get(0).get(0); + RawTextObject header = (RawTextObject) block.getRows().get(0).get(0); assertThat(header.getType(), is("raw_text")); assertThat(header.getText(), is("Team")); @@ -1797,7 +1797,7 @@ public void parseDataTableBlock() { assertTrue(richCell instanceof RichTextBlock); assertThat(richCell.getType(), is("rich_text")); - RawNumberTableCell numberCell = (RawNumberTableCell) block.getRows().get(1).get(1); + RawNumberObject numberCell = (RawNumberObject) block.getRows().get(1).get(1); assertThat(numberCell.getType(), is("raw_number")); assertThat(numberCell.getValue(), is(1234.5)); assertThat(numberCell.getText(), is("$1,234.50")); @@ -1812,12 +1812,12 @@ public void buildDataTableBlock() { .rowHeaderColumnIndex(0) .rows(Arrays.asList( Arrays.asList( - RawTextTableCell.builder().text("Team").build(), - RawTextTableCell.builder().text("Revenue").build() + RawTextObject.builder().text("Team").build(), + RawTextObject.builder().text("Revenue").build() ), Arrays.asList( - RawTextTableCell.builder().text("Platform").build(), - RawNumberTableCell.builder().value(1234.5).text("$1,234.50").build() + RawTextObject.builder().text("Platform").build(), + RawNumberObject.builder().value(1234.5).text("$1,234.50").build() ) ))); assertThat(block, is(notNullValue())); @@ -1829,7 +1829,7 @@ public void buildDataTableBlock() { assertThat(restored.getCaption(), is("Quarterly results")); assertThat(restored.getPageSize(), is(10)); assertThat(restored.getRows().size(), is(2)); - RawNumberTableCell numberCell = (RawNumberTableCell) restored.getRows().get(1).get(1); + RawNumberObject numberCell = (RawNumberObject) restored.getRows().get(1).get(1); assertThat(numberCell.getValue(), is(1234.5)); } From 393dd2215a16dc771fd7df105da91f9f914e6d9a Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 20:14:51 -0700 Subject: [PATCH 06/11] refactor(block-kit): tidy data_table factory order and RichTextBlock import - GsonLayoutBlockFactory: move the DataTableBlock case into alphabetical order (between ContextBlock and DividerBlock) instead of after SectionBlock. - RichTextBlock: import com.slack.api.model.block.composition.TableCell and use the simple name in the implements clause rather than the fully qualified type inline. Co-Authored-By: Claude --- .../main/java/com/slack/api/model/block/RichTextBlock.java | 3 ++- .../java/com/slack/api/util/json/GsonLayoutBlockFactory.java | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java b/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java index 19bd1bd46..0af63bb7b 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/RichTextBlock.java @@ -1,5 +1,6 @@ package com.slack.api.model.block; +import com.slack.api.model.block.composition.TableCell; import com.slack.api.model.block.element.BlockElement; import lombok.AllArgsConstructor; import lombok.Builder; @@ -16,7 +17,7 @@ @Builder @NoArgsConstructor @AllArgsConstructor -public class RichTextBlock implements LayoutBlock, com.slack.api.model.block.composition.TableCell { +public class RichTextBlock implements LayoutBlock, TableCell { public static final String TYPE = "rich_text"; private final String type = TYPE; @Builder.Default diff --git a/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java b/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java index 00db145f2..9fc4f7605 100644 --- a/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java +++ b/slack-api-model/src/main/java/com/slack/api/util/json/GsonLayoutBlockFactory.java @@ -49,6 +49,8 @@ private Class getLayoutClassInstance(String typeName) { return ContextActionsBlock.class; case ContextBlock.TYPE: return ContextBlock.class; + case DataTableBlock.TYPE: + return DataTableBlock.class; case DividerBlock.TYPE: return DividerBlock.class; case FileBlock.TYPE: @@ -65,8 +67,6 @@ private Class getLayoutClassInstance(String typeName) { return RichTextBlock.class; case SectionBlock.TYPE: return SectionBlock.class; - case DataTableBlock.TYPE: - return DataTableBlock.class; case ShareShortcutBlock.TYPE: return ShareShortcutBlock.class; case TaskCardBlock.TYPE: From f6e66bf81e05ded2043538b21f631ec641c73c9c Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 20:23:29 -0700 Subject: [PATCH 07/11] docs(block-kit): correct RawNumberObject text as required, not optional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The raw_number schema marks text as required (required: [type, value, text], text minLength 1) — the javadoc wrongly called it optional. Match the schema: text carries the display representation of the value and must be at least one character long. Co-Authored-By: Claude --- .../slack/api/model/block/composition/RawNumberObject.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java index 460efa6c6..7c576061e 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java @@ -6,8 +6,9 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_number} table cell, holding a numeric value. The optional {@code text} - * field carries the display representation of the value (for example a formatted string). + * A {@code raw_number} table cell, holding a numeric value. The {@code text} field carries + * the text used to display the value (for example a formatted string) and must be at least + * one character long. * * @see Table block * @see Data table block From e417c315149564613547cfa0d910e32d8358028b Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 20:30:08 -0700 Subject: [PATCH 08/11] docs(block-kit): drop the parenthetical example from RawNumberObject Keep the text-is-display + min-length note, drop the "(for example a formatted string)" aside. Co-Authored-By: Claude --- .../com/slack/api/model/block/composition/RawNumberObject.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java index 7c576061e..74bd17961 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java @@ -7,8 +7,7 @@ /** * A {@code raw_number} table cell, holding a numeric value. The {@code text} field carries - * the text used to display the value (for example a formatted string) and must be at least - * one character long. + * the text used to display the value and must be at least one character long. * * @see Table block * @see Data table block From 36396ef1bc3d1cc9c819d468f8aafd7a674d1dbb Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 20:34:58 -0700 Subject: [PATCH 09/11] docs(block-kit): document raw cell fields per-field, matching node Restructure RawNumberObject and RawTextObject to per-field javadoc, mirroring the node @slack/types descriptions and matching this repo's own per-field convention (OptionObject, ConfirmationDialogObject): - class: "Defines an object containing a numeric value." / "...some text." - value: "The numeric value." - text (number): "The text used to display the value. The minimum length is 1 character." - text (text): "The text for the block. The minimum length is 1 character." Co-Authored-By: Claude --- .../api/model/block/composition/RawNumberObject.java | 11 +++++++++-- .../api/model/block/composition/RawTextObject.java | 6 +++++- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java index 74bd17961..8a71bf4a1 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawNumberObject.java @@ -6,8 +6,7 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_number} table cell, holding a numeric value. The {@code text} field carries - * the text used to display the value and must be at least one character long. + * Defines an object containing a numeric value. * * @see Table block * @see Data table block @@ -19,6 +18,14 @@ public class RawNumberObject implements TableCell { public static final String TYPE = "raw_number"; private final String type = TYPE; + + /** + * The numeric value. + */ private Double value; + + /** + * The text used to display the value. The minimum length is 1 character. + */ private String text; } diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java index 46a710b25..aa3a1a6f5 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/RawTextObject.java @@ -6,7 +6,7 @@ import lombok.NoArgsConstructor; /** - * A {@code raw_text} table cell, holding unformatted plain text. + * Defines an object containing some text. * * @see Table block * @see Data table block @@ -18,5 +18,9 @@ public class RawTextObject implements TableCell { public static final String TYPE = "raw_text"; private final String type = TYPE; + + /** + * The text for the block. The minimum length is 1 character. + */ private String text; } From ec6733e880fd542161f58a0e65d505d6a6f7da01 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 21:00:03 -0700 Subject: [PATCH 10/11] feat(block-kit): add rawText / rawNumber BlockCompositions helpers Add BlockCompositions.rawText(...) and rawNumber(...) factory helpers for the raw table cell objects, mirroring the plainText / markdownText DSL (configurator overload plus a convenience overload). Gives callers an ergonomic way to build data_table / table cells without the raw builder. Co-Authored-By: Claude --- .../block/composition/BlockCompositions.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java index f6413c5f0..7e2d86a69 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java @@ -54,6 +54,26 @@ public static MarkdownTextObject markdownText(String text) { return MarkdownTextObject.builder().text(text).build(); } + // RawNumberObject + + public static RawNumberObject rawNumber(ModelConfigurator configurator) { + return configurator.configure(RawNumberObject.builder()).build(); + } + + public static RawNumberObject rawNumber(double value, String text) { + return RawNumberObject.builder().value(value).text(text).build(); + } + + // RawTextObject + + public static RawTextObject rawText(ModelConfigurator configurator) { + return configurator.configure(RawTextObject.builder()).build(); + } + + public static RawTextObject rawText(String text) { + return RawTextObject.builder().text(text).build(); + } + // OptionGroupObject public static OptionGroupObject optionGroup(ModelConfigurator configurator) { From 60a4781b1c2e3b8195e1f808e01e5b471202b7a9 Mon Sep 17 00:00:00 2001 From: Eden Zimbelman Date: Tue, 8 Sep 2026 21:10:51 -0700 Subject: [PATCH 11/11] refactor(block-kit): take Double in rawNumber helper to match the model RawNumberObject.value is a (nullable) Double; the two-arg rawNumber convenience overload took a primitive double, narrowing the model's type. Use Double so the helper mirrors the field exactly. Co-Authored-By: Claude --- .../slack/api/model/block/composition/BlockCompositions.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java b/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java index 7e2d86a69..e09e12ce5 100644 --- a/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java +++ b/slack-api-model/src/main/java/com/slack/api/model/block/composition/BlockCompositions.java @@ -60,7 +60,7 @@ public static RawNumberObject rawNumber(ModelConfigurator