diff --git a/integration/rendering.test.tsx b/integration/rendering.test.tsx
index 170e940..521c3c5 100644
--- a/integration/rendering.test.tsx
+++ b/integration/rendering.test.tsx
@@ -5,7 +5,14 @@ import React from "react"
import { omit } from "../src/helpers/omit.js"
import { raise } from "../src/helpers/raise.js"
import type { ReacordRoot } from "../src/main.js"
-import { Button, createRoot, Embed, EmbedField, Text } from "../src/main.js"
+import {
+ ActionRow,
+ Button,
+ createRoot,
+ Embed,
+ EmbedField,
+ Text,
+} from "../src/main.js"
import { testBotToken, testChannelId } from "./test-environment.js"
const client = new Client({
@@ -121,6 +128,9 @@ test("kitchen sink", async () => {
complex button text
+
+
+
>,
)
await assertMessages([
@@ -212,6 +222,17 @@ test("kitchen sink", async () => {
},
],
},
+ {
+ type: "ACTION_ROW",
+ components: [
+ {
+ type: "BUTTON",
+ label: "new action row",
+ style: "SECONDARY",
+ disabled: false,
+ },
+ ],
+ },
],
},
])
diff --git a/src/action-row.tsx b/src/action-row.tsx
new file mode 100644
index 0000000..b82be05
--- /dev/null
+++ b/src/action-row.tsx
@@ -0,0 +1,45 @@
+import type {
+ MessageActionRowComponentOptions,
+ MessageOptions,
+} from "discord.js"
+import React from "react"
+import { ContainerInstance } from "./container-instance.js"
+
+export type ActionRowProps = {
+ children: React.ReactNode
+}
+
+export function ActionRow(props: ActionRowProps) {
+ return (
+ new ActionRowInstance()}>
+ {props.children}
+
+ )
+}
+
+class ActionRowInstance extends ContainerInstance {
+ readonly name = "ActionRow"
+
+ constructor() {
+ super({ warnOnNonTextChildren: false })
+ }
+
+ // eslint-disable-next-line class-methods-use-this
+ override renderToMessage(options: MessageOptions) {
+ const row = {
+ type: "ACTION_ROW" as const,
+ components: [] as MessageActionRowComponentOptions[],
+ }
+
+ for (const child of this.children) {
+ if (!child.renderToActionRow) {
+ console.warn(`${child.name} is not an action row component`)
+ continue
+ }
+ child.renderToActionRow(row)
+ }
+
+ options.components ??= []
+ options.components.push(row)
+ }
+}
diff --git a/src/base-instance.ts b/src/base-instance.ts
index d426db1..10187da 100644
--- a/src/base-instance.ts
+++ b/src/base-instance.ts
@@ -1,4 +1,8 @@
-import type { MessageEmbedOptions, MessageOptions } from "discord.js"
+import type {
+ MessageActionRowOptions,
+ MessageEmbedOptions,
+ MessageOptions,
+} from "discord.js"
export abstract class BaseInstance {
/** The name of the JSX element represented by this instance */
@@ -14,4 +18,8 @@ export abstract class BaseInstance {
/** If this element can be a child of an embed,
* the function to modify the embed options */
renderToEmbed?(options: MessageEmbedOptions): void
+
+ /** If this element can be a child of an action row,
+ * the function to modify the action row options */
+ renderToActionRow?(options: MessageActionRowOptions): void
}
diff --git a/src/button.tsx b/src/button.tsx
index 6e942e2..04ac77b 100644
--- a/src/button.tsx
+++ b/src/button.tsx
@@ -2,6 +2,7 @@ import type {
BaseMessageComponentOptions,
EmojiResolvable,
MessageActionRowOptions,
+ MessageButtonOptions,
MessageButtonStyle,
MessageOptions,
} from "discord.js"
@@ -36,6 +37,17 @@ class ButtonInstance extends ContainerInstance {
super({ warnOnNonTextChildren: true })
}
+ private getButtonOptions(): Required &
+ MessageButtonOptions {
+ return {
+ ...pick(this.props, "emoji", "disabled"),
+ type: "BUTTON",
+ style: this.props.style ? toUpper(this.props.style) : "SECONDARY",
+ label: this.getChildrenText(),
+ customId: nanoid(),
+ }
+ }
+
override renderToMessage(options: MessageOptions) {
options.components ??= []
@@ -56,12 +68,10 @@ class ButtonInstance extends ContainerInstance {
options.components.push(actionRow)
}
- actionRow.components.push({
- ...pick(this.props, "emoji", "disabled"),
- type: "BUTTON",
- style: this.props.style ? toUpper(this.props.style) : "SECONDARY",
- label: this.getChildrenText(),
- customId: nanoid(),
- })
+ actionRow.components.push(this.getButtonOptions())
+ }
+
+ override renderToActionRow(row: MessageActionRowOptions) {
+ row.components.push(this.getButtonOptions())
}
}
diff --git a/src/main.ts b/src/main.ts
index 7d56fd2..e9e235c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,7 @@
-export * from "./button"
-export * from "./embed"
-export * from "./embed-field"
-export * from "./root"
-export * from "./text"
+/* eslint-disable import/no-unused-modules */
+export * from "./action-row.js"
+export * from "./button.js"
+export * from "./embed-field.js"
+export * from "./embed.js"
+export * from "./root.js"
+export * from "./text.js"