Skip to content

feat(model): add table Block Kit block - #1611

Open
zimeg wants to merge 15 commits into
chore/block-kit-data-tablefrom
chore/block-kit-table
Open

zimeg wants to merge 15 commits into
chore/block-kit-data-tablefrom
chore/block-kit-table

Conversation

@zimeg

@zimeg zimeg commented Jun 26, 2026 •

Copy link
Copy Markdown
Member

This pull request adds the table layout block to the Java SDK model.

  • TableBlock (table) with rows, columnSettings, and blockId.
  • TableBlock.ColumnSetting, nested since only table uses it, with align (left, center, right) and isWrapped.
  • rows is List<List<TableCell>>, using the shared RawTextObject (raw_text), RawNumberObject (raw_number), and RichTextBlock (rich_text) cells from feat(block-kit): add data_table layout block #1613.
  • TableBlock is registered in GsonLayoutBlockFactory. Cells use the GsonTableCellFactory from feat(block-kit): add data_table layout block #1613, so no new Gson registration.
  • Blocks.table(...) builder helper.
  • BlockKitTest parses the docs example, with an added raw_number row, and re-serializes it to the same JSON. It also round-trips a built block.

This depends on #1613 and targets its branch so the diff shows only the table changes. The base moves back to main when #1613 merges.

Kotlin DSL support is left for a follow-up that covers every block the Kotlin extension doesn't have yet.

Testing

  1. Post the docs example with chat.postMessage:

    import static com.slack.api.model.block.Blocks.*;
    import static com.slack.api.model.block.composition.BlockCompositions.*;
    
    ChatPostMessageResponse response = slack.methods(token).chatPostMessage(r -> r
        .channel(channelId)
        .text("A table")
        .blocks(asBlocks(table(t -> t
            .columnSettings(Arrays.asList(
                TableBlock.ColumnSetting.builder().isWrapped(true).build(),
                TableBlock.ColumnSetting.builder().align("right").build()
            ))
            .rows(Arrays.asList(
                Arrays.asList(rawText("Header A"), rawText("Header B")),
                Arrays.asList(rawText("Data 1A"), rawNumber(1234.5, "$1,234.50"))
            ))))));
  2. Confirm response.isOk() and that the table renders with the first column wrapped and the second column right aligned.

  3. Read the message back with conversations.history and confirm the block parses as a TableBlock with RawTextObject and RawNumberObject cells.

🤖 Generated with Claude Code

Add support for the `table` layout block, which renders tabular data as a
grid of rows and cells. Cells may be `raw_text`, `raw_number`, or a
`rich_text` block, with optional per-column alignment/wrapping settings.

- TableBlock (type "table") with rows, columnSettings, blockId
- TableCell interface + RawTextTableCell, RawNumberTableCell impls;
  RichTextBlock now also implements TableCell for rich_text cells
- TableColumnSetting (align, isWrapped)
- GsonTableCellFactory for polymorphic cell (de)serialization, registered
  in GsonFactory; TableBlock registered in GsonLayoutBlockFactory
- Blocks.table(...) builder helper
- Tests covering parse, round-trip, and builder usage

Docs: https://docs.slack.dev/reference/block-kit/blocks/table-block

Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
@codecov

codecov Bot commented Jun 26, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.73%. Comparing base (75b0c01) to head (872276b).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@                       Coverage Diff                        @@
##             chore/block-kit-data-table    #1611      +/-   ##
================================================================
+ Coverage                         73.72%   73.73%   +0.01%     
- Complexity                         4687     4690       +3     
================================================================
  Files                               488      488              
  Lines                             14695    14697       +2     
  Branches                           1529     1529              
================================================================
+ Hits                              10834    10837       +3     
+ Misses                             2952     2951       -1     
  Partials                            909      909              
Flag Coverage Δ
jdk-14 73.73% <100.00%> (+0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

@kevin-boban-glean

Copy link
Copy Markdown

@zimeg Thanks for this PR. Can this be merged please to bring parity?

@srtaalej srtaalej added enhancement M-T: A feature request for new functionality semver:minor labels Aug 28, 2026

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Leaving some thoughts on the first shot implementation that I think we might want to consider before merge. I'm curious if we start with a less complete implementation - table but no cells - for sake of aligning the data_table block properties but I haven't investigated too much more.

zimeg added a commit that referenced this pull request Sep 9, 2026
…ta_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<List<TableCell>>; 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 <svc-devxp-claude@slack-corp.com>
@kaanayden-carbon

kaanayden-carbon commented Oct 21, 2026 •

Copy link
Copy Markdown

We'd love to see this merged! We currently publish table rows data as chunked tables in separate messages inside markdown blocks because of the markdown text limit. Would be great to also use the additional features of the table block that markdown tables don't have!

zimeg and others added 3 commits November 4, 2026 18:04
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
build the table block on the shared RawTextObject and RawNumberObject cells from the data_table branch, drop the duplicate RawTextTableCell and RawNumberTableCell types, mirror the table-block docs in javadoc, and round-trip the docs example in BlockKitTest
@zimeg zimeg added the project:slack-api-model project:slack-api-model label Nov 5, 2026
@zimeg zimeg added this to the 1.54.1 milestone Nov 5, 2026
@zimeg
zimeg changed the base branch from main to chore/block-kit-data-table November 5, 2026 22:52
zimeg and others added 7 commits November 5, 2026 15:50
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude <svc-devxp-claude@slack-corp.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@zimeg zimeg left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚙️ A comment about perhaps design decision but I'm open to separate classes if imports make more sense of course!

Comment on lines +2837 to +2840
.columnSettings(Arrays.asList(
TableBlock.ColumnSetting.builder().isWrapped(true).build(),
TableBlock.ColumnSetting.builder().align("right").build()
))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The 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 TableBlock class! Here's how it appears in actual code.

@zimeg
zimeg requested a review from a team as a code owner November 6, 2026 05:25

@WilliamBergamin WilliamBergamin left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 Thanks @zimeg! Reusing the shared cells from #1613 keeps this tiny, and the parse test re-serializing the docs example to the identical JSON tree pins the wire format nicely.
Left a few comments, nothing blocking 💯

* 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<>();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧩 question (non-blocking): rows takes any TableCell, which includes ActionCellObject, so a table can build (and GsonTableCellFactory will parse) an action_cell that the table block spec doesn't allow. Right now only the TableCell Javadoc says it's for data tables. What do you think about a narrower cell type for table rows so the type carries that rule? Fair to skip if a second factory isn't worth it 🤔

" \"blocks\": [\n" +
" {\n" +
" \"type\": \"table\",\n" +
" \"column_settings\": [\n" +

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧪 suggestion (non-blocking): the spec says a null entry in column_settings skips that column. Could we add a null to this fixture? The existing toJsonTree round-trip would pin it for free.

Comment on lines +25 to +28
/**
* 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}.
*/

Copy link
Copy Markdown
Contributor

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 @see already links, so it can drift. Same idea for the // TableBlock marker in Blocks.java and the docs URL comment in the test.

Suggested change
/**
* 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}.
*/

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement M-T: A feature request for new functionality project:slack-api-model project:slack-api-model semver:minor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants