From 2a708396eb6c248c7bba50d5fe2e9911dbff78c9 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Thu, 27 Aug 2026 16:08:05 -0400 Subject: [PATCH 1/2] feat: add carousel block example --- block-kit/src/main/java/blocks/Carousel.java | 54 ++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 block-kit/src/main/java/blocks/Carousel.java diff --git a/block-kit/src/main/java/blocks/Carousel.java b/block-kit/src/main/java/blocks/Carousel.java new file mode 100644 index 0000000..23e4dc2 --- /dev/null +++ b/block-kit/src/main/java/blocks/Carousel.java @@ -0,0 +1,54 @@ +package blocks; + +import com.slack.api.model.block.Blocks; +import com.slack.api.model.block.CarouselBlock; +import com.slack.api.model.block.composition.BlockCompositions; +import com.slack.api.model.block.element.BlockElements; +import java.util.List; + +/** + * Displays a horizontally scrollable collection of cards. + * {@link https://docs.slack.dev/reference/block-kit/blocks/carousel-block/} + */ +public class Carousel { + /** + * A carousel with three cards. + */ + public static CarouselBlock example01() { + CarouselBlock block = Blocks.carousel(c -> c.elements(List.of( + Blocks.card(card -> card.blockId("carousel-card-1") + .icon(BlockElements.imageElement( + i -> i.imageUrl("https://picsum.photos/36/36").altText("Icon"))) + .title(BlockCompositions.markdownText("MDR")) + .subtitle(BlockCompositions.markdownText("Refining data files")) + .heroImage(BlockElements.imageElement( + i -> i.imageUrl("https://picsum.photos/400/300").altText("Sample hero image"))) + .body(BlockCompositions.markdownText("Blue badge required to gain access.")) + .actions(List.of(BlockElements.button(b -> b.text(BlockCompositions.plainText( + pt -> pt.text("Action Button").emoji(false))) + .actionId("button_action_1"))))), + Blocks.card(card -> card.blockId("carousel-card-2") + .icon(BlockElements.imageElement( + i -> i.imageUrl("https://picsum.photos/36/36").altText("Icon"))) + .title(BlockCompositions.markdownText("O&D")) + .subtitle(BlockCompositions.markdownText("Storage, maintenance, and rotation of art pieces")) + .heroImage(BlockElements.imageElement( + i -> i.imageUrl("https://picsum.photos/400/300").altText("Sample hero image"))) + .body(BlockCompositions.markdownText("Green badge required to gain access.")) + .actions(List.of(BlockElements.button(b -> b.text(BlockCompositions.plainText( + pt -> pt.text("Action Button").emoji(false))) + .actionId("button_action_2"))))), + Blocks.card(card -> card.blockId("carousel-card-3") + .icon(BlockElements.imageElement( + i -> i.imageUrl("https://picsum.photos/36/36").altText("Icon"))) + .title(BlockCompositions.markdownText("Wellness Center")) + .subtitle(BlockCompositions.markdownText("Wellness sessions")) + .heroImage(BlockElements.imageElement( + i -> i.imageUrl("https://picsum.photos/400/300").altText("Sample hero image"))) + .body(BlockCompositions.markdownText("Please take a seat in the waiting room until called.")) + .actions(List.of(BlockElements.button(b -> b.text(BlockCompositions.plainText( + pt -> pt.text("Action Button").emoji(false))) + .actionId("button_action_3")))))))); + return block; + } +} From b0087d9ac3e97322df5c43512eb46d5c2aaa82e6 Mon Sep 17 00:00:00 2001 From: Ale Mercado Date: Fri, 28 Aug 2026 10:29:25 -0400 Subject: [PATCH 2/2] test: add carousel block test, update readme and docstring --- block-kit/README.md | 1 + block-kit/src/main/java/blocks/Carousel.java | 2 +- .../src/test/java/blocks/CarouselTest.java | 135 ++++++++++++++++++ 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 block-kit/src/test/java/blocks/CarouselTest.java diff --git a/block-kit/README.md b/block-kit/README.md index 3cd3768..302ad39 100644 --- a/block-kit/README.md +++ b/block-kit/README.md @@ -9,6 +9,7 @@ Read the [docs](https://docs.slack.dev/block-kit/) to learn concepts behind thes ### Blocks - **[Actions](https://docs.slack.dev/reference/block-kit/blocks/actions-block)**: Holds multiple interactive elements. [Implementation](./src/main/java/blocks/Actions.java). +- **[Carousel](https://docs.slack.dev/reference/block-kit/blocks/carousel-block)**: Displays related card blocks in a horizontally-scrolling container. [Implementation](./src/main/java/blocks/Carousel.java). - **[Context](https://docs.slack.dev/reference/block-kit/blocks/context-block)**: Provides contextual info, which can include both images and text. [Implementation](./src/main/java/blocks/Context.java). - **[Divider](https://docs.slack.dev/reference/block-kit/blocks/divider-block)**: Visually separates pieces of info inside of a message. [Implementation](./src/main/java/blocks/Divider.java). - **[File](https://docs.slack.dev/reference/block-kit/blocks/file-block)**: Displays info about remote files. [Implementation](./src/main/java/blocks/File.java). diff --git a/block-kit/src/main/java/blocks/Carousel.java b/block-kit/src/main/java/blocks/Carousel.java index 23e4dc2..e9d6581 100644 --- a/block-kit/src/main/java/blocks/Carousel.java +++ b/block-kit/src/main/java/blocks/Carousel.java @@ -7,7 +7,7 @@ import java.util.List; /** - * Displays a horizontally scrollable collection of cards. + * Displays related card blocks in a horizontally-scrolling container. * {@link https://docs.slack.dev/reference/block-kit/blocks/carousel-block/} */ public class Carousel { diff --git a/block-kit/src/test/java/blocks/CarouselTest.java b/block-kit/src/test/java/blocks/CarouselTest.java new file mode 100644 index 0000000..90a84f5 --- /dev/null +++ b/block-kit/src/test/java/blocks/CarouselTest.java @@ -0,0 +1,135 @@ +package blocks; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import com.google.gson.JsonParser; +import com.slack.api.model.block.CarouselBlock; +import com.slack.api.util.json.GsonFactory; +import org.junit.jupiter.api.Test; + +public class CarouselTest { + @Test + public void testExample01() { + CarouselBlock block = Carousel.example01(); + String actual = GsonFactory.createSnakeCase().toJson(block); + String expected = """ + { + "type": "carousel", + "elements": [ + { + "type": "card", + "block_id": "carousel-card-1", + "icon": { + "type": "image", + "image_url": "https://picsum.photos/36/36", + "alt_text": "Icon" + }, + "title": { + "type": "mrkdwn", + "text": "MDR" + }, + "subtitle": { + "type": "mrkdwn", + "text": "Refining data files" + }, + "hero_image": { + "type": "image", + "image_url": "https://picsum.photos/400/300", + "alt_text": "Sample hero image" + }, + "body": { + "type": "mrkdwn", + "text": "Blue badge required to gain access." + }, + "actions": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Action Button", + "emoji": false + }, + "action_id": "button_action_1" + } + ] + }, + { + "type": "card", + "block_id": "carousel-card-2", + "icon": { + "type": "image", + "image_url": "https://picsum.photos/36/36", + "alt_text": "Icon" + }, + "title": { + "type": "mrkdwn", + "text": "O&D" + }, + "subtitle": { + "type": "mrkdwn", + "text": "Storage, maintenance, and rotation of art pieces" + }, + "hero_image": { + "type": "image", + "image_url": "https://picsum.photos/400/300", + "alt_text": "Sample hero image" + }, + "body": { + "type": "mrkdwn", + "text": "Green badge required to gain access." + }, + "actions": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Action Button", + "emoji": false + }, + "action_id": "button_action_2" + } + ] + }, + { + "type": "card", + "block_id": "carousel-card-3", + "icon": { + "type": "image", + "image_url": "https://picsum.photos/36/36", + "alt_text": "Icon" + }, + "title": { + "type": "mrkdwn", + "text": "Wellness Center" + }, + "subtitle": { + "type": "mrkdwn", + "text": "Wellness sessions" + }, + "hero_image": { + "type": "image", + "image_url": "https://picsum.photos/400/300", + "alt_text": "Sample hero image" + }, + "body": { + "type": "mrkdwn", + "text": "Please take a seat in the waiting room until called." + }, + "actions": [ + { + "type": "button", + "text": { + "type": "plain_text", + "text": "Action Button", + "emoji": false + }, + "action_id": "button_action_3" + } + ] + } + ] + } + """; + assertEquals(JsonParser.parseString(expected), JsonParser.parseString(actual)); + } +}