Skip to content

Commit ace834b

Browse files
fix: Adjust label predicates and fix buttons emoji/label behaviour (#11317)
* fix: buttons with custom id * fix: button labels * fix: also add refinement to link buttons --------- Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
1 parent d59857e commit ace834b

File tree

3 files changed

+59
-22
lines changed

3 files changed

+59
-22
lines changed

packages/builders/__tests__/components/button.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,13 @@ import {
55
type APIButtonComponentWithURL,
66
} from 'discord-api-types/v10';
77
import { describe, test, expect } from 'vitest';
8-
import { PrimaryButtonBuilder, PremiumButtonBuilder, LinkButtonBuilder } from '../../src/index.js';
8+
import {
9+
PrimaryButtonBuilder,
10+
PremiumButtonBuilder,
11+
LinkButtonBuilder,
12+
DangerButtonBuilder,
13+
SecondaryButtonBuilder,
14+
} from '../../src/index.js';
915

1016
const longStr =
1117
'looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong';
@@ -25,6 +31,26 @@ describe('Button Components', () => {
2531
button.toJSON();
2632
}).not.toThrowError();
2733

34+
expect(() => {
35+
const button = new SecondaryButtonBuilder().setCustomId('custom').setLabel('a'.repeat(80));
36+
button.toJSON();
37+
}).not.toThrowError();
38+
39+
expect(() => {
40+
const button = new DangerButtonBuilder().setCustomId('custom').setEmoji({ name: 'ok' });
41+
button.toJSON();
42+
}).not.toThrowError();
43+
44+
expect(() => {
45+
const button = new LinkButtonBuilder().setURL('https://discord.js.org').setLabel('a'.repeat(80));
46+
button.toJSON();
47+
}).not.toThrowError();
48+
49+
expect(() => {
50+
const button = new LinkButtonBuilder().setURL('https://discord.js.org').setEmoji({ name: 'ok' });
51+
button.toJSON();
52+
}).not.toThrowError();
53+
2854
expect(() => {
2955
const button = new PremiumButtonBuilder().setSKUId('123456789012345678');
3056
button.toJSON();

packages/builders/__tests__/components/selectMenu.test.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const selectMenuWithId = () => new StringSelectMenuBuilder({ custom_id: 'hi' });
77
const selectMenuOption = () => new StringSelectMenuOptionBuilder();
88

99
const longStr = 'a'.repeat(256);
10+
const selectMenuOptionLabelAboveLimit = 'a'.repeat(101);
11+
const selectMenuOptionValueAboveLimit = 'a'.repeat(101);
12+
const selectMenuOptionDescriptionAboveLimit = 'a'.repeat(101);
1013

1114
const selectMenuOptionData: APISelectMenuOption = {
1215
label: 'test',
@@ -196,13 +199,13 @@ describe('Select Menu Components', () => {
196199

197200
expect(() => {
198201
selectMenuOption()
199-
.setLabel(longStr)
200-
.setValue(longStr)
202+
.setLabel(selectMenuOptionLabelAboveLimit)
203+
.setValue(selectMenuOptionValueAboveLimit)
201204
// @ts-expect-error: Invalid default value
202205
.setDefault(-1)
203206
// @ts-expect-error: Invalid emoji
204207
.setEmoji({ name: 1 })
205-
.setDescription(longStr)
208+
.setDescription(selectMenuOptionDescriptionAboveLimit)
206209
.toJSON();
207210
}).toThrowError();
208211
});

packages/builders/src/components/Assertions.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import { ButtonStyle, ChannelType, ComponentType, SelectMenuDefaultValueType } f
22
import { z } from 'zod';
33
import { idPredicate, customIdPredicate, snowflakePredicate } from '../Assertions.js';
44

5-
const labelPredicate = z.string().min(1).max(80);
6-
75
export const emojiPredicate = z
86
.strictObject({
97
id: snowflakePredicate.optional(),
@@ -19,23 +17,33 @@ const buttonPredicateBase = z.strictObject({
1917
disabled: z.boolean().optional(),
2018
});
2119

22-
const buttonCustomIdPredicateBase = buttonPredicateBase.extend({
23-
custom_id: customIdPredicate,
24-
emoji: emojiPredicate.optional(),
25-
label: labelPredicate,
26-
});
20+
const buttonLabelPredicate = z.string().min(1).max(80);
2721

28-
const buttonPrimaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Primary) });
29-
const buttonSecondaryPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Secondary) });
30-
const buttonSuccessPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Success) });
31-
const buttonDangerPredicate = buttonCustomIdPredicateBase.extend({ style: z.literal(ButtonStyle.Danger) });
22+
const buttonCustomIdPredicateBase = buttonPredicateBase
23+
.extend({
24+
custom_id: customIdPredicate,
25+
emoji: emojiPredicate.optional(),
26+
label: buttonLabelPredicate.optional(),
27+
})
28+
.refine((data) => data.emoji !== undefined || data.label !== undefined, {
29+
message: 'Buttons with a custom id must have either an emoji or a label.',
30+
});
3231

33-
const buttonLinkPredicate = buttonPredicateBase.extend({
34-
style: z.literal(ButtonStyle.Link),
35-
url: z.url({ protocol: /^(?:https?|discord)$/ }).max(512),
36-
emoji: emojiPredicate.optional(),
37-
label: labelPredicate,
38-
});
32+
const buttonPrimaryPredicate = buttonCustomIdPredicateBase.safeExtend({ style: z.literal(ButtonStyle.Primary) });
33+
const buttonSecondaryPredicate = buttonCustomIdPredicateBase.safeExtend({ style: z.literal(ButtonStyle.Secondary) });
34+
const buttonSuccessPredicate = buttonCustomIdPredicateBase.safeExtend({ style: z.literal(ButtonStyle.Success) });
35+
const buttonDangerPredicate = buttonCustomIdPredicateBase.safeExtend({ style: z.literal(ButtonStyle.Danger) });
36+
37+
const buttonLinkPredicate = buttonPredicateBase
38+
.extend({
39+
style: z.literal(ButtonStyle.Link),
40+
url: z.url({ protocol: /^(?:https?|discord)$/ }).max(512),
41+
emoji: emojiPredicate.optional(),
42+
label: buttonLabelPredicate.optional(),
43+
})
44+
.refine((data) => data.emoji !== undefined || data.label !== undefined, {
45+
message: 'Link buttons must have either an emoji or a label.',
46+
});
3947

4048
const buttonPremiumPredicate = buttonPredicateBase.extend({
4149
style: z.literal(ButtonStyle.Premium),
@@ -92,7 +100,7 @@ export const selectMenuRolePredicate = selectMenuBasePredicate.extend({
92100
});
93101

94102
export const selectMenuStringOptionPredicate = z.object({
95-
label: labelPredicate,
103+
label: z.string().min(1).max(100),
96104
value: z.string().min(1).max(100),
97105
description: z.string().min(1).max(100).optional(),
98106
emoji: emojiPredicate.optional(),

0 commit comments

Comments
 (0)