mirror of
https://github.com/discordjs/discord.js.git
synced 2026-03-12 09:33:32 +01:00
feat(builders): add checkbox, checkboxgroup, and radiogroup builders (#11410)
* feat(builders): add checkbox, checkboxgroup, and radiogroup builders * Update packages/builders/src/components/checkbox/Assertions.ts fix incorrect wording about default option count in radio groups Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> * fix(builders): remove length validators from add/splice options * chore: remove directives * fix(builders): documentation fixes * fix(builders): return Result.err instead of throw in validators --------- Co-authored-by: Qjuh <76154676+Qjuh@users.noreply.github.com> Co-authored-by: Jiralite <33201955+Jiralite@users.noreply.github.com>
This commit is contained in:
415
packages/builders/__tests__/components/checkbox.test.ts
Normal file
415
packages/builders/__tests__/components/checkbox.test.ts
Normal file
@@ -0,0 +1,415 @@
|
||||
import { ComponentType, type APICheckboxComponent } from 'discord-api-types/v10';
|
||||
import { describe, test, expect } from 'vitest';
|
||||
import { LabelBuilder } from '../../src';
|
||||
import { CheckboxBuilder } from '../../src/components/checkbox/Checkbox';
|
||||
import { CheckboxGroupBuilder } from '../../src/components/checkbox/CheckboxGroup';
|
||||
import { CheckboxGroupOptionBuilder } from '../../src/components/checkbox/CheckboxGroupOption';
|
||||
import { RadioGroupBuilder } from '../../src/components/checkbox/RadioGroup';
|
||||
import { RadioGroupOptionBuilder } from '../../src/components/checkbox/RadioGroupOption';
|
||||
|
||||
const longStr = ':3'.repeat(5_000);
|
||||
|
||||
const fiveCheckboxOptions = [
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
|
||||
];
|
||||
|
||||
const elevenCheckboxOptions = [
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 6').setValue('option_6'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 7').setValue('option_7'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 8').setValue('option_8'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 9').setValue('option_9'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 10').setValue('option_10'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 11').setValue('option_11'),
|
||||
];
|
||||
|
||||
const fiveRadioOptions = [
|
||||
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
|
||||
];
|
||||
|
||||
const elevenRadioOptions = [
|
||||
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 3').setValue('option_3'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 4').setValue('option_4'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 5').setValue('option_5'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 6').setValue('option_6'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 7').setValue('option_7'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 8').setValue('option_8'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 9').setValue('option_9'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 10').setValue('option_10'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 11').setValue('option_11'),
|
||||
];
|
||||
|
||||
describe('Checkbox Components', () => {
|
||||
describe('CheckboxBuilder', () => {
|
||||
test('Valid builder does not throw.', () => {
|
||||
expect(() => new CheckboxBuilder().setCustomId('checkbox').toJSON()).not.toThrowError();
|
||||
expect(() => new CheckboxBuilder().setCustomId('checkbox').setDefault(true).toJSON()).not.toThrowError();
|
||||
});
|
||||
|
||||
test('Invalid builder does throw.', () => {
|
||||
expect(() => new CheckboxBuilder().toJSON()).toThrowError();
|
||||
expect(() => new CheckboxBuilder().setDefault(true).toJSON()).toThrowError();
|
||||
expect(() => new CheckboxBuilder().setCustomId(longStr).toJSON()).toThrowError();
|
||||
});
|
||||
|
||||
test('API data equals toJSON().', () => {
|
||||
const checkboxData = {
|
||||
type: ComponentType.Checkbox,
|
||||
custom_id: 'checkbox',
|
||||
default: true,
|
||||
} satisfies APICheckboxComponent;
|
||||
|
||||
expect(new CheckboxBuilder(checkboxData).toJSON()).toEqual(checkboxData);
|
||||
|
||||
expect(new CheckboxBuilder().setCustomId('checkbox').setDefault(true).toJSON()).toEqual(checkboxData);
|
||||
});
|
||||
});
|
||||
|
||||
describe('CheckboxGroupBuilder', () => {
|
||||
test('Valid builder does not throw.', () => {
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder({
|
||||
custom_id: 'checkbox_group',
|
||||
options: fiveCheckboxOptions.map((option) => option.toJSON()),
|
||||
}).toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions(fiveCheckboxOptions).toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions([
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
])
|
||||
.toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setCustomId('checkbox_group').addOptions(fiveCheckboxOptions).toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setMinValues(1)
|
||||
.setMaxValues(2)
|
||||
.setOptions([
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
])
|
||||
.toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.spliceOptions(2, 1, ...elevenCheckboxOptions.slice(7, 9))
|
||||
.spliceOptions(0, 1, { label: 'New Option', value: 'new_option' }),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('Invalid builder does throw.', () => {
|
||||
expect(() => new CheckboxGroupBuilder().toJSON()).toThrowError();
|
||||
expect(() => new CheckboxGroupBuilder().addOptions([]).toJSON()).toThrowError();
|
||||
expect(() => new CheckboxGroupBuilder().setMinValues(2).setMaxValues(1).toJSON()).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setMinValues(2).setMaxValues(1).addOptions(fiveCheckboxOptions).toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setMinValues(2)
|
||||
.setMaxValues(1)
|
||||
.addOptions(fiveCheckboxOptions)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setCustomId('checkbox_group').setMinValues(5).setMaxValues(11).toJSON(),
|
||||
).toThrowError();
|
||||
expect(() => new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions([]).toJSON()).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.setMinValues(6)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.setMaxValues(6)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions(elevenCheckboxOptions).toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(elevenCheckboxOptions)
|
||||
.setMaxValues(12)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setCustomId(longStr).setOptions(fiveCheckboxOptions).toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.setMinValues(0)
|
||||
.setRequired(true)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setMaxValues(4)
|
||||
.setOptions([
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1').setDefault(true),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 2').setValue('option_2').setDefault(true),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3').setDefault(true),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 4').setValue('option_4').setDefault(true),
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 5').setValue('option_5').setDefault(true),
|
||||
])
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.addOptions(fiveCheckboxOptions)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(elevenCheckboxOptions.slice(0, 5))
|
||||
.addOptions(elevenCheckboxOptions.slice(5, 11))
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(
|
||||
() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.spliceOptions(2, 1, new CheckboxGroupOptionBuilder().setLabel('Option 6')), // no value
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder()
|
||||
.setCustomId('checkbox_group')
|
||||
.setOptions(fiveCheckboxOptions)
|
||||
.spliceOptions(2, 1, { value: 'hi', label: longStr }),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupBuilder().setCustomId('checkbox_group').addOptions({ value: 'hi', label: longStr }),
|
||||
).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('CheckboxGroupOptionBuilder', () => {
|
||||
test('Valid builder does not throw.', () => {
|
||||
expect(() =>
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue('option_1').toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupOptionBuilder()
|
||||
.setLabel('Option 2')
|
||||
.setValue('option_2')
|
||||
.setDescription('This is option 2')
|
||||
.toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new CheckboxGroupOptionBuilder().setLabel('Option 3').setValue('option_3').setDefault(true).toJSON(),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('Invalid builder does throw.', () => {
|
||||
expect(() => new CheckboxGroupOptionBuilder().toJSON()).toThrowError();
|
||||
expect(() => new CheckboxGroupOptionBuilder().setValue('option_1').toJSON()).toThrowError();
|
||||
expect(() => new CheckboxGroupOptionBuilder().setLabel('Option 1').toJSON()).toThrowError();
|
||||
expect(() => new CheckboxGroupOptionBuilder().setLabel(longStr).setValue('option_1').toJSON()).toThrowError();
|
||||
expect(() => new CheckboxGroupOptionBuilder().setLabel('Option 1').setValue(longStr).toJSON()).toThrowError();
|
||||
});
|
||||
|
||||
test('toJSON returns correct data.', () => {
|
||||
const option = new CheckboxGroupOptionBuilder()
|
||||
.setLabel('Option 1')
|
||||
.setValue('option_1')
|
||||
.setDescription('This is option 1')
|
||||
.setDefault(true);
|
||||
|
||||
expect(option.toJSON()).toEqual({
|
||||
label: 'Option 1',
|
||||
value: 'option_1',
|
||||
description: 'This is option 1',
|
||||
default: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('RadioGroupBuilder', () => {
|
||||
test('Valid builder does not throw.', () => {
|
||||
expect(() =>
|
||||
new RadioGroupBuilder().setCustomId('radio_group').addOptions(fiveRadioOptions).toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.setOptions([
|
||||
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1'),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2'),
|
||||
])
|
||||
.toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder().setCustomId('radio_group').addOptions(fiveRadioOptions).setRequired(false),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder().setCustomId('radio_group').addOptions(fiveRadioOptions).setRequired(true),
|
||||
).not.toThrowError();
|
||||
expect(() => new RadioGroupBuilder().setCustomId('radio_group').setOptions(fiveRadioOptions)).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.setOptions(fiveRadioOptions)
|
||||
.spliceOptions(2, 1, elevenRadioOptions.slice(7, 9)),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.addOptions(elevenRadioOptions.slice(0, 5))
|
||||
.spliceOptions(0, 1, { label: 'New Option', value: 'new_option' }),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder({
|
||||
custom_id: 'radio_group',
|
||||
options: fiveRadioOptions.map((option) => option.toJSON()),
|
||||
}).toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.addOptions(fiveRadioOptions.map((option) => option.toJSON()))
|
||||
.toJSON(),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('Invalid builder does throw.', () => {
|
||||
expect(() => new RadioGroupBuilder().toJSON()).toThrowError();
|
||||
expect(() => new RadioGroupBuilder().addOptions([]).toJSON()).toThrowError();
|
||||
// needs at least 2 options
|
||||
expect(() => new RadioGroupBuilder().addOptions([fiveRadioOptions[0]]).toJSON()).toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder().setCustomId('radio_group').setOptions([fiveRadioOptions[0]]).toJSON(),
|
||||
).toThrowError();
|
||||
expect(() => new RadioGroupBuilder().setCustomId('radio_group').setOptions([]).toJSON()).toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder().setCustomId('radio_group').setOptions(elevenRadioOptions).toJSON(),
|
||||
).toThrowError();
|
||||
expect(() => new RadioGroupBuilder().setCustomId(longStr).setOptions(fiveRadioOptions).toJSON()).toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.setOptions([
|
||||
new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1').setDefault(true),
|
||||
new RadioGroupOptionBuilder().setLabel('Option 2').setValue('option_2').setDefault(true),
|
||||
])
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.addOptions(fiveRadioOptions)
|
||||
.addOptions(fiveRadioOptions)
|
||||
.toJSON(),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder()
|
||||
.setCustomId('radio_group')
|
||||
.setOptions(fiveRadioOptions)
|
||||
.spliceOptions(2, 1, { value: 'hi', label: longStr }),
|
||||
).toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupBuilder().setCustomId('radio_group').addOptions({ value: 'hi', label: longStr }),
|
||||
).toThrowError();
|
||||
});
|
||||
});
|
||||
|
||||
describe('RadioGroupOptionBuilder', () => {
|
||||
test('Valid builder does not throw.', () => {
|
||||
expect(() => new RadioGroupOptionBuilder().setLabel('Option 1').setValue('option_1').toJSON()).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupOptionBuilder()
|
||||
.setLabel('Option 2')
|
||||
.setValue('option_2')
|
||||
.setDescription('This is option 2')
|
||||
.toJSON(),
|
||||
).not.toThrowError();
|
||||
expect(() =>
|
||||
new RadioGroupOptionBuilder().setLabel('Option 3').setValue('option_3').setDefault(true).toJSON(),
|
||||
).not.toThrowError();
|
||||
});
|
||||
|
||||
test('Invalid builder does throw.', () => {
|
||||
expect(() => new RadioGroupOptionBuilder().toJSON()).toThrowError();
|
||||
expect(() => new RadioGroupOptionBuilder().setValue('option_1').toJSON()).toThrowError();
|
||||
expect(() => new RadioGroupOptionBuilder().setLabel('Option 1').toJSON()).toThrowError();
|
||||
expect(() => new RadioGroupOptionBuilder().setLabel(longStr).setValue('option_1').toJSON()).toThrowError();
|
||||
expect(() => new RadioGroupOptionBuilder().setLabel('Option 1').setValue(longStr).toJSON()).toThrowError();
|
||||
});
|
||||
|
||||
test('toJSON returns correct data.', () => {
|
||||
const option = new RadioGroupOptionBuilder()
|
||||
.setLabel('Option 1')
|
||||
.setValue('option_1')
|
||||
.setDescription('This is option 1')
|
||||
.setDefault(true);
|
||||
|
||||
expect(option.toJSON()).toEqual({
|
||||
label: 'Option 1',
|
||||
value: 'option_1',
|
||||
description: 'This is option 1',
|
||||
default: true,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('LabelBuilder with Checkbox Components', () => {
|
||||
test('LabelBuilder can set Checkbox component.', () => {
|
||||
const checkbox = new CheckboxBuilder().setCustomId('checkbox').setDefault(true);
|
||||
const label = new LabelBuilder().setLabel('Checkbox Label').setCheckboxComponent(checkbox);
|
||||
expect(() => label.toJSON()).not.toThrowError();
|
||||
expect(label.toJSON().component).toEqual(checkbox.toJSON());
|
||||
});
|
||||
|
||||
test('LabelBuilder can set CheckboxGroup component.', () => {
|
||||
const checkboxGroup = new CheckboxGroupBuilder().setCustomId('checkbox_group').setOptions(fiveCheckboxOptions);
|
||||
const label = new LabelBuilder().setLabel('Checkbox Group Label').setCheckboxGroupComponent(checkboxGroup);
|
||||
expect(() => label.toJSON()).not.toThrowError();
|
||||
expect(label.toJSON().component).toEqual(checkboxGroup.toJSON());
|
||||
});
|
||||
|
||||
test('LabelBuilder can set RadioGroup component.', () => {
|
||||
const radioGroup = new RadioGroupBuilder().setCustomId('radio_group').setOptions(fiveRadioOptions);
|
||||
const label = new LabelBuilder().setLabel('Radio Group Label').setRadioGroupComponent(radioGroup);
|
||||
expect(() => label.toJSON()).not.toThrowError();
|
||||
expect(label.toJSON().component).toEqual(radioGroup.toJSON());
|
||||
});
|
||||
});
|
||||
@@ -8,6 +8,9 @@ import {
|
||||
} from './ActionRow.js';
|
||||
import { ComponentBuilder } from './Component.js';
|
||||
import { ButtonBuilder } from './button/Button.js';
|
||||
import { CheckboxBuilder } from './checkbox/Checkbox.js';
|
||||
import { CheckboxGroupBuilder } from './checkbox/CheckboxGroup.js';
|
||||
import { RadioGroupBuilder } from './checkbox/RadioGroup.js';
|
||||
import { FileUploadBuilder } from './fileUpload/FileUpload.js';
|
||||
import { LabelBuilder } from './label/Label.js';
|
||||
import { ChannelSelectMenuBuilder } from './selectMenu/ChannelSelectMenu.js';
|
||||
@@ -110,6 +113,18 @@ export interface MappedComponentTypes {
|
||||
* The file upload component type is associated with a {@link FileUploadBuilder}.
|
||||
*/
|
||||
[ComponentType.FileUpload]: FileUploadBuilder;
|
||||
/**
|
||||
* The checkbox component type is associated with a {@link CheckboxBuilder}.
|
||||
*/
|
||||
[ComponentType.Checkbox]: CheckboxBuilder;
|
||||
/**
|
||||
* The checkbox group component type is associated with a {@link CheckboxGroupBuilder}.
|
||||
*/
|
||||
[ComponentType.CheckboxGroup]: CheckboxGroupBuilder;
|
||||
/**
|
||||
* The radio group component type is associated with a {@link RadioGroupBuilder}.
|
||||
*/
|
||||
[ComponentType.RadioGroup]: RadioGroupBuilder;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -175,8 +190,14 @@ export function createComponentBuilder(
|
||||
return new LabelBuilder(data);
|
||||
case ComponentType.FileUpload:
|
||||
return new FileUploadBuilder(data);
|
||||
case ComponentType.Checkbox:
|
||||
return new CheckboxBuilder(data);
|
||||
case ComponentType.CheckboxGroup:
|
||||
return new CheckboxGroupBuilder(data);
|
||||
case ComponentType.RadioGroup:
|
||||
return new RadioGroupBuilder(data);
|
||||
default:
|
||||
// https://github.com/discordjs/discord.js/pull/11410
|
||||
// @ts-expect-error This case can still occur if we get a newer unsupported component type
|
||||
throw new Error(`Cannot properly serialize component type: ${data.type}`);
|
||||
}
|
||||
}
|
||||
|
||||
98
packages/builders/src/components/checkbox/Assertions.ts
Normal file
98
packages/builders/src/components/checkbox/Assertions.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { Result, s } from '@sapphire/shapeshift';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { isValidationEnabled } from '../../util/validation';
|
||||
import { customIdValidator, idValidator } from '../Assertions';
|
||||
|
||||
export const checkboxPredicate = s
|
||||
.object({
|
||||
type: s.literal(ComponentType.Checkbox),
|
||||
custom_id: customIdValidator,
|
||||
id: idValidator.optional(),
|
||||
default: s.boolean().optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const checkboxGroupOptionPredicate = s
|
||||
.object({
|
||||
label: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
|
||||
value: s.string().lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(100),
|
||||
description: s.string().lengthLessThanOrEqual(100).optional(),
|
||||
default: s.boolean().optional(),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const checkboxGroupPredicate = s
|
||||
.object({
|
||||
type: s.literal(ComponentType.CheckboxGroup),
|
||||
custom_id: customIdValidator,
|
||||
id: idValidator.optional(),
|
||||
options: s.array(checkboxGroupOptionPredicate).lengthGreaterThanOrEqual(1).lengthLessThanOrEqual(10),
|
||||
min_values: s.number().int().greaterThanOrEqual(0).lessThanOrEqual(10).optional(),
|
||||
max_values: s.number().int().greaterThanOrEqual(1).lessThanOrEqual(10).optional(),
|
||||
required: s.boolean().optional(),
|
||||
})
|
||||
.reshape((data) => {
|
||||
// Ensure min_values is not greater than max_values
|
||||
if (data.min_values !== undefined && data.max_values !== undefined && data.min_values > data.max_values) {
|
||||
return Result.err(new RangeError('min_values cannot be greater than max_values'));
|
||||
}
|
||||
|
||||
// Ensure max_values is not greater than the number of options
|
||||
if (data.max_values !== undefined && data.max_values > data.options.length) {
|
||||
return Result.err(new RangeError('max_values cannot be greater than the number of options'));
|
||||
}
|
||||
|
||||
// Ensure min_values is not greater than the number of options
|
||||
if (data.min_values !== undefined && data.min_values > data.options.length) {
|
||||
return Result.err(new RangeError('min_values cannot be greater than the number of options'));
|
||||
}
|
||||
|
||||
// Ensure required is consistent with min_values
|
||||
if (data.required === true && data.min_values === 0) {
|
||||
return Result.err(new RangeError('If required is true, min_values must be at least 1'));
|
||||
}
|
||||
|
||||
// Ensure there are not more default values than max_values
|
||||
const defaultCount = data.options.filter((option) => option.default === true).length;
|
||||
if (data.max_values !== undefined && defaultCount > data.max_values) {
|
||||
return Result.err(new RangeError('The number of default options cannot be greater than max_values'));
|
||||
}
|
||||
|
||||
// Ensure each option's value is unique
|
||||
const values = data.options.map((option) => option.value);
|
||||
const uniqueValues = new Set(values);
|
||||
if (uniqueValues.size !== values.length) {
|
||||
return Result.err(new RangeError('Each option in a checkbox group must have a unique value'));
|
||||
}
|
||||
|
||||
return Result.ok(data);
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
export const radioGroupOptionPredicate = checkboxGroupOptionPredicate;
|
||||
|
||||
export const radioGroupPredicate = s
|
||||
.object({
|
||||
type: s.literal(ComponentType.RadioGroup),
|
||||
custom_id: customIdValidator,
|
||||
id: idValidator.optional(),
|
||||
options: s.array(radioGroupOptionPredicate).lengthGreaterThanOrEqual(2).lengthLessThanOrEqual(10),
|
||||
required: s.boolean().optional(),
|
||||
})
|
||||
.reshape((data) => {
|
||||
// Ensure there is exactly one default option
|
||||
const defaultCount = data.options.filter((option) => option.default === true).length;
|
||||
if (defaultCount > 1) {
|
||||
return Result.err(new RangeError('There can be at most one default option in a radio group'));
|
||||
}
|
||||
|
||||
// Ensure each option's value is unique
|
||||
const values = data.options.map((option) => option.value);
|
||||
const uniqueValues = new Set(values);
|
||||
if (uniqueValues.size !== values.length) {
|
||||
return Result.err(new RangeError('Each option in a radio group must have a unique value'));
|
||||
}
|
||||
|
||||
return Result.ok(data);
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
63
packages/builders/src/components/checkbox/Checkbox.ts
Normal file
63
packages/builders/src/components/checkbox/Checkbox.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import type { APICheckboxComponent } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { checkboxPredicate } from './Assertions';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for checkboxes.
|
||||
*/
|
||||
export class CheckboxBuilder extends ComponentBuilder<APICheckboxComponent> {
|
||||
/**
|
||||
* Creates a new checkbox from API data.
|
||||
*
|
||||
* @param data - The API data to create this checkbox with
|
||||
* @example
|
||||
* Creating a checkbox from an API data object:
|
||||
* ```ts
|
||||
* const checkbox = new CheckboxBuilder({
|
||||
* custom_id: 'accept_terms',
|
||||
* default: false,
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a checkbox using setters and API data:
|
||||
* ```ts
|
||||
* const checkbox = new CheckboxBuilder()
|
||||
* .setCustomId('subscribe_newsletter')
|
||||
* .setDefault(true);
|
||||
* ```
|
||||
*/
|
||||
public constructor(data?: Partial<APICheckboxComponent>) {
|
||||
super({ type: ComponentType.Checkbox, ...data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom id of this checkbox.
|
||||
*
|
||||
* @param customId - The custom id to use
|
||||
*/
|
||||
public setCustomId(customId: string) {
|
||||
this.data.custom_id = customId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this checkbox is checked by default.
|
||||
*
|
||||
* @param isDefault - Whether the checkbox should be checked by default
|
||||
*/
|
||||
public setDefault(isDefault: boolean) {
|
||||
this.data.default = isDefault;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APICheckboxComponent {
|
||||
checkboxPredicate.parse(this.data);
|
||||
return {
|
||||
...this.data,
|
||||
} as APICheckboxComponent;
|
||||
}
|
||||
}
|
||||
174
packages/builders/src/components/checkbox/CheckboxGroup.ts
Normal file
174
packages/builders/src/components/checkbox/CheckboxGroup.ts
Normal file
@@ -0,0 +1,174 @@
|
||||
import type { APICheckboxGroupComponent, APICheckboxGroupOption } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import type { RestOrArray } from '../../util/normalizeArray';
|
||||
import { normalizeArray } from '../../util/normalizeArray';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { checkboxGroupOptionPredicate, checkboxGroupPredicate } from './Assertions';
|
||||
import { CheckboxGroupOptionBuilder } from './CheckboxGroupOption';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for checkbox groups.
|
||||
*/
|
||||
export class CheckboxGroupBuilder extends ComponentBuilder<APICheckboxGroupComponent> {
|
||||
/**
|
||||
* The options within this checkbox group.
|
||||
*/
|
||||
public readonly options: CheckboxGroupOptionBuilder[];
|
||||
|
||||
/**
|
||||
* Creates a new checkbox group from API data.
|
||||
*
|
||||
* @param data - The API data to create this checkbox group with
|
||||
* @example
|
||||
* Creating a checkbox group from an API data object:
|
||||
* ```ts
|
||||
* const checkboxGroup = new CheckboxGroupBuilder({
|
||||
* custom_id: 'select_options',
|
||||
* options: [
|
||||
* { label: 'Option 1', value: 'option_1' },
|
||||
* { label: 'Option 2', value: 'option_2' },
|
||||
* ],
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a checkbox group using setters and API data:
|
||||
* ```ts
|
||||
* const checkboxGroup = new CheckboxGroupBuilder()
|
||||
* .setCustomId('choose_items')
|
||||
* .setOptions([
|
||||
* { label: 'Item A', value: 'item_a' },
|
||||
* { label: 'Item B', value: 'item_b' },
|
||||
* ])
|
||||
* .setMinValues(1)
|
||||
* .setMaxValues(2);
|
||||
* ```
|
||||
*/
|
||||
public constructor(data?: Partial<APICheckboxGroupComponent>) {
|
||||
const { options, ...initData } = data ?? {};
|
||||
super({ ...initData, type: ComponentType.CheckboxGroup });
|
||||
this.options = options?.map((option: APICheckboxGroupOption) => new CheckboxGroupOptionBuilder(option)) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom id of this checkbox group.
|
||||
*
|
||||
* @param customId - The custom id to use
|
||||
*/
|
||||
public setCustomId(customId: string) {
|
||||
this.data.custom_id = customId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds options to this checkbox group.
|
||||
*
|
||||
* @param options - The options to add
|
||||
*/
|
||||
public addOptions(...options: RestOrArray<APICheckboxGroupOption | CheckboxGroupOptionBuilder>) {
|
||||
const normalizedOptions = normalizeArray(options);
|
||||
|
||||
this.options.push(
|
||||
...normalizedOptions.map((normalizedOption) => {
|
||||
// I do this because TS' duck typing causes issues,
|
||||
// if I put in a RadioGroupOption, TS lets it pass but
|
||||
// it fails to convert to a checkbox group option at runtime
|
||||
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
|
||||
const option = new CheckboxGroupOptionBuilder(json);
|
||||
checkboxGroupOptionPredicate.parse(option.toJSON());
|
||||
return option;
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options for this checkbox group.
|
||||
*
|
||||
* @param options - The options to use
|
||||
*/
|
||||
public setOptions(options: RestOrArray<APICheckboxGroupOption | CheckboxGroupOptionBuilder>) {
|
||||
return this.spliceOptions(0, this.options.length, ...options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, or inserts options for this checkbox group.
|
||||
*
|
||||
* @remarks
|
||||
* This method behaves similarly
|
||||
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
|
||||
* It's useful for modifying and adjusting the order of existing options.
|
||||
* @param index - The index to start at
|
||||
* @param deleteCount - The number of options to remove
|
||||
* @param options - The replacing option objects or builders
|
||||
*/
|
||||
public spliceOptions(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...options: RestOrArray<APICheckboxGroupOption | CheckboxGroupOptionBuilder>
|
||||
) {
|
||||
const normalizedOptions = normalizeArray(options);
|
||||
|
||||
const clone = [...this.options];
|
||||
|
||||
clone.splice(
|
||||
index,
|
||||
deleteCount,
|
||||
...normalizedOptions.map((normalizedOption) => {
|
||||
// I do this because TS' duck typing causes issues,
|
||||
// if I put in a RadioGroupOption, TS lets it pass but
|
||||
// it fails to convert to a checkbox group option at runtime
|
||||
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
|
||||
const option = new CheckboxGroupOptionBuilder(json);
|
||||
checkboxGroupOptionPredicate.parse(option.toJSON());
|
||||
return option;
|
||||
}),
|
||||
);
|
||||
|
||||
this.options.splice(0, this.options.length, ...clone);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum number of options that must be selected.
|
||||
*
|
||||
* @param minValues - The minimum number of options that must be selected
|
||||
*/
|
||||
public setMinValues(minValues: number) {
|
||||
this.data.min_values = minValues;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the maximum number of options that can be selected.
|
||||
*
|
||||
* @param maxValues - The maximum number of options that can be selected
|
||||
*/
|
||||
public setMaxValues(maxValues: number) {
|
||||
this.data.max_values = maxValues;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether selecting options is required.
|
||||
*
|
||||
* @param required - Whether selecting options is required
|
||||
*/
|
||||
public setRequired(required: boolean) {
|
||||
this.data.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APICheckboxGroupComponent {
|
||||
const data = {
|
||||
...this.data,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
|
||||
checkboxGroupPredicate.parse(data);
|
||||
|
||||
return data as APICheckboxGroupComponent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import type { JSONEncodable } from '@discordjs/util';
|
||||
import type { APICheckboxGroupOption } from 'discord-api-types/v10';
|
||||
import { checkboxGroupOptionPredicate } from './Assertions';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for checkbox group options.
|
||||
*/
|
||||
export class CheckboxGroupOptionBuilder implements JSONEncodable<APICheckboxGroupOption> {
|
||||
/**
|
||||
* Creates a new checkbox group option from API data.
|
||||
*
|
||||
* @param data - The API data to create this checkbox group option with
|
||||
* @example
|
||||
* Creating a checkbox group option from an API data object:
|
||||
* ```ts
|
||||
* const option = new CheckboxGroupOptionBuilder({
|
||||
* label: 'Option 1',
|
||||
* value: 'option_1',
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a checkbox group option using setters and API data:
|
||||
* ```ts
|
||||
* const option = new CheckboxGroupOptionBuilder()
|
||||
* .setLabel('Option 2')
|
||||
* .setValue('option_2');
|
||||
* ```
|
||||
*/
|
||||
public constructor(public data: Partial<APICheckboxGroupOption> = {}) {}
|
||||
|
||||
/**
|
||||
* Sets the label for this option.
|
||||
*
|
||||
* @param label - The label to use
|
||||
*/
|
||||
public setLabel(label: string) {
|
||||
this.data.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for this option.
|
||||
*
|
||||
* @param value - The value to use
|
||||
*/
|
||||
public setValue(value: string) {
|
||||
this.data.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description for this option.
|
||||
*
|
||||
* @param description - The description to use
|
||||
*/
|
||||
public setDescription(description: string) {
|
||||
this.data.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this option is selected by default.
|
||||
*
|
||||
* @param isDefault - Whether the option should be selected by default
|
||||
*/
|
||||
public setDefault(isDefault: boolean) {
|
||||
this.data.default = isDefault;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public toJSON(): APICheckboxGroupOption {
|
||||
checkboxGroupOptionPredicate.parse(this.data);
|
||||
|
||||
return {
|
||||
...this.data,
|
||||
} as APICheckboxGroupOption;
|
||||
}
|
||||
}
|
||||
152
packages/builders/src/components/checkbox/RadioGroup.ts
Normal file
152
packages/builders/src/components/checkbox/RadioGroup.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
import type { APIRadioGroupComponent, APIRadioGroupOption } from 'discord-api-types/v10';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import type { RestOrArray } from '../../util/normalizeArray';
|
||||
import { normalizeArray } from '../../util/normalizeArray';
|
||||
import { ComponentBuilder } from '../Component';
|
||||
import { radioGroupOptionPredicate, radioGroupPredicate } from './Assertions';
|
||||
import { RadioGroupOptionBuilder } from './RadioGroupOption';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for radio groups.
|
||||
*/
|
||||
export class RadioGroupBuilder extends ComponentBuilder<APIRadioGroupComponent> {
|
||||
/**
|
||||
* The options within this radio group.
|
||||
*/
|
||||
public readonly options: RadioGroupOptionBuilder[];
|
||||
|
||||
/**
|
||||
* Creates a new radio group from API data.
|
||||
*
|
||||
* @param data - The API data to create this radio group with
|
||||
* @example
|
||||
* Creating a radio group from an API data object:
|
||||
* ```ts
|
||||
* const radioGroup = new RadioGroupBuilder({
|
||||
* custom_id: 'select_options',
|
||||
* options: [
|
||||
* { label: 'Option 1', value: 'option_1' },
|
||||
* { label: 'Option 2', value: 'option_2' },
|
||||
* ],
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a radio group using setters and API data:
|
||||
* ```ts
|
||||
* const radioGroup = new RadioGroupBuilder()
|
||||
* .setCustomId('choose_items')
|
||||
* .setOptions([
|
||||
* { label: 'Item A', value: 'item_a' },
|
||||
* { label: 'Item B', value: 'item_b' },
|
||||
* ])
|
||||
* ```
|
||||
*/
|
||||
public constructor(data?: Partial<APIRadioGroupComponent>) {
|
||||
const { options, ...initData } = data ?? {};
|
||||
super({ ...initData, type: ComponentType.RadioGroup });
|
||||
this.options = options?.map((option: APIRadioGroupOption) => new RadioGroupOptionBuilder(option)) ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the custom id of this radio group.
|
||||
*
|
||||
* @param customId - The custom id to use
|
||||
*/
|
||||
public setCustomId(customId: string) {
|
||||
this.data.custom_id = customId;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds options to this radio group.
|
||||
*
|
||||
* @param options - The options to add
|
||||
*/
|
||||
public addOptions(...options: RestOrArray<APIRadioGroupOption | RadioGroupOptionBuilder>) {
|
||||
const normalizedOptions = normalizeArray(options);
|
||||
|
||||
this.options.push(
|
||||
...normalizedOptions.map((normalizedOption) => {
|
||||
// I do this because TS' duck typing causes issues,
|
||||
// if I put in a CheckboxGroupOption, TS lets it pass but
|
||||
// it fails to convert to a checkbox group option at runtime
|
||||
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
|
||||
const option = new RadioGroupOptionBuilder(json);
|
||||
radioGroupOptionPredicate.parse(option.toJSON());
|
||||
return option;
|
||||
}),
|
||||
);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the options for this radio group.
|
||||
*
|
||||
* @param options - The options to use
|
||||
*/
|
||||
public setOptions(options: RestOrArray<APIRadioGroupOption | RadioGroupOptionBuilder>) {
|
||||
return this.spliceOptions(0, this.options.length, ...options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes, replaces, or inserts options for this radio group.
|
||||
*
|
||||
* @remarks
|
||||
* This method behaves similarly
|
||||
* to {@link https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array/splice | Array.prototype.splice()}.
|
||||
* It's useful for modifying and adjusting the order of existing options.
|
||||
* @param index - The index to start at
|
||||
* @param deleteCount - The number of options to remove
|
||||
* @param options - The replacing option objects or builders
|
||||
*/
|
||||
public spliceOptions(
|
||||
index: number,
|
||||
deleteCount: number,
|
||||
...options: RestOrArray<APIRadioGroupOption | RadioGroupOptionBuilder>
|
||||
) {
|
||||
const normalizedOptions = normalizeArray(options);
|
||||
|
||||
const clone = [...this.options];
|
||||
|
||||
clone.splice(
|
||||
index,
|
||||
deleteCount,
|
||||
...normalizedOptions.map((normalizedOption) => {
|
||||
// I do this because TS' duck typing causes issues,
|
||||
// if I put in a CheckboxGroupOption, TS lets it pass but
|
||||
// it fails to convert to a radio group option at runtime
|
||||
const json = 'toJSON' in normalizedOption ? normalizedOption.toJSON() : normalizedOption;
|
||||
const option = new RadioGroupOptionBuilder(json);
|
||||
radioGroupOptionPredicate.parse(option.toJSON());
|
||||
return option;
|
||||
}),
|
||||
);
|
||||
|
||||
this.options.splice(0, this.options.length, ...clone);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether selecting options is required.
|
||||
*
|
||||
* @param required - Whether selecting options is required
|
||||
*/
|
||||
public setRequired(required: boolean) {
|
||||
this.data.required = required;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public override toJSON(): APIRadioGroupComponent {
|
||||
const data = {
|
||||
...this.data,
|
||||
options: this.options.map((option) => option.toJSON()),
|
||||
};
|
||||
|
||||
radioGroupPredicate.parse(data);
|
||||
|
||||
return data as APIRadioGroupComponent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import type { JSONEncodable } from '@discordjs/util';
|
||||
import type { APIRadioGroupOption } from 'discord-api-types/v10';
|
||||
import { radioGroupOptionPredicate } from './Assertions';
|
||||
|
||||
/**
|
||||
* A builder that creates API-compatible JSON data for radio group options.
|
||||
*/
|
||||
export class RadioGroupOptionBuilder implements JSONEncodable<APIRadioGroupOption> {
|
||||
/**
|
||||
* Creates a new radio group option from API data.
|
||||
*
|
||||
* @param data - The API data to create this radio group option with
|
||||
* @example
|
||||
* Creating a radio group option from an API data object:
|
||||
* ```ts
|
||||
* const option = new RadioGroupOptionBuilder({
|
||||
* label: 'Option 1',
|
||||
* value: 'option_1',
|
||||
* });
|
||||
* ```
|
||||
* @example
|
||||
* Creating a radio group option using setters and API data:
|
||||
* ```ts
|
||||
* const option = new RadioGroupOptionBuilder()
|
||||
* .setLabel('Option 2')
|
||||
* .setValue('option_2');
|
||||
* ```
|
||||
*/
|
||||
public constructor(public data: Partial<APIRadioGroupOption> = {}) {}
|
||||
|
||||
/**
|
||||
* Sets the label for this option.
|
||||
*
|
||||
* @param label - The label to use
|
||||
*/
|
||||
public setLabel(label: string) {
|
||||
this.data.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value for this option.
|
||||
*
|
||||
* @param value - The value to use
|
||||
*/
|
||||
public setValue(value: string) {
|
||||
this.data.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the description for this option.
|
||||
*
|
||||
* @param description - The description to use
|
||||
*/
|
||||
public setDescription(description: string) {
|
||||
this.data.description = description;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether this option is selected by default.
|
||||
*
|
||||
* @param isDefault - Whether the option should be selected by default
|
||||
*/
|
||||
public setDefault(isDefault: boolean) {
|
||||
this.data.default = isDefault;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
public toJSON(): APIRadioGroupOption {
|
||||
radioGroupOptionPredicate.parse(this.data);
|
||||
|
||||
return {
|
||||
...this.data,
|
||||
} as APIRadioGroupOption;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import { s } from '@sapphire/shapeshift';
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { isValidationEnabled } from '../../util/validation.js';
|
||||
import { idValidator } from '../Assertions.js';
|
||||
import { checkboxGroupPredicate, checkboxPredicate, radioGroupPredicate } from '../checkbox/Assertions.js';
|
||||
import { fileUploadPredicate } from '../fileUpload/Assertions.js';
|
||||
import {
|
||||
selectMenuChannelPredicate,
|
||||
@@ -26,6 +27,9 @@ export const labelPredicate = s
|
||||
selectMenuChannelPredicate,
|
||||
selectMenuStringPredicate,
|
||||
fileUploadPredicate,
|
||||
checkboxPredicate,
|
||||
checkboxGroupPredicate,
|
||||
radioGroupPredicate,
|
||||
]),
|
||||
})
|
||||
.setValidationEnabled(isValidationEnabled);
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import type {
|
||||
APIChannelSelectComponent,
|
||||
APICheckboxComponent,
|
||||
APICheckboxGroupComponent,
|
||||
APIFileUploadComponent,
|
||||
APILabelComponent,
|
||||
APIMentionableSelectComponent,
|
||||
APIRadioGroupComponent,
|
||||
APIRoleSelectComponent,
|
||||
APIStringSelectComponent,
|
||||
APITextInputComponent,
|
||||
@@ -11,6 +14,9 @@ import type {
|
||||
import { ComponentType } from 'discord-api-types/v10';
|
||||
import { ComponentBuilder } from '../Component.js';
|
||||
import { createComponentBuilder, resolveBuilder } from '../Components.js';
|
||||
import { CheckboxBuilder } from '../checkbox/Checkbox.js';
|
||||
import { CheckboxGroupBuilder } from '../checkbox/CheckboxGroup.js';
|
||||
import { RadioGroupBuilder } from '../checkbox/RadioGroup.js';
|
||||
import { FileUploadBuilder } from '../fileUpload/FileUpload.js';
|
||||
import { ChannelSelectMenuBuilder } from '../selectMenu/ChannelSelectMenu.js';
|
||||
import { MentionableSelectMenuBuilder } from '../selectMenu/MentionableSelectMenu.js';
|
||||
@@ -23,8 +29,11 @@ import { labelPredicate } from './Assertions.js';
|
||||
export interface LabelBuilderData extends Partial<Omit<APILabelComponent, 'component'>> {
|
||||
component?:
|
||||
| ChannelSelectMenuBuilder
|
||||
| CheckboxBuilder
|
||||
| CheckboxGroupBuilder
|
||||
| FileUploadBuilder
|
||||
| MentionableSelectMenuBuilder
|
||||
| RadioGroupBuilder
|
||||
| RoleSelectMenuBuilder
|
||||
| StringSelectMenuBuilder
|
||||
| TextInputBuilder
|
||||
@@ -68,7 +77,6 @@ export class LabelBuilder extends ComponentBuilder<LabelBuilderData> {
|
||||
|
||||
this.data = {
|
||||
...rest,
|
||||
// @ts-expect-error https://github.com/discordjs/discord.js/pull/11410
|
||||
component: component ? createComponentBuilder(component) : undefined,
|
||||
type: ComponentType.Label,
|
||||
};
|
||||
@@ -195,6 +203,42 @@ export class LabelBuilder extends ComponentBuilder<LabelBuilderData> {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a checkbox component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setCheckboxComponent(
|
||||
input: APICheckboxComponent | CheckboxBuilder | ((builder: CheckboxBuilder) => CheckboxBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, CheckboxBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a checkbox group component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setCheckboxGroupComponent(
|
||||
input: APICheckboxGroupComponent | CheckboxGroupBuilder | ((builder: CheckboxGroupBuilder) => CheckboxGroupBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, CheckboxGroupBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a radio group component to this label.
|
||||
*
|
||||
* @param input - A function that returns a component builder or an already built builder
|
||||
*/
|
||||
public setRadioGroupComponent(
|
||||
input: APIRadioGroupComponent | RadioGroupBuilder | ((builder: RadioGroupBuilder) => RadioGroupBuilder),
|
||||
): this {
|
||||
this.data.component = resolveBuilder(input, RadioGroupBuilder);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc ComponentBuilder.toJSON}
|
||||
*/
|
||||
|
||||
@@ -51,6 +51,13 @@ export * from './components/v2/Separator.js';
|
||||
export * from './components/v2/TextDisplay.js';
|
||||
export * from './components/v2/Thumbnail.js';
|
||||
|
||||
export * from './components/checkbox/Checkbox.js';
|
||||
export * from './components/checkbox/CheckboxGroup.js';
|
||||
export * from './components/checkbox/CheckboxGroupOption.js';
|
||||
export * from './components/checkbox/RadioGroup.js';
|
||||
export * from './components/checkbox/RadioGroupOption.js';
|
||||
export * as CheckboxAssertions from './components/checkbox/Assertions.js';
|
||||
|
||||
export * as SlashCommandAssertions from './interactions/slashCommands/Assertions.js';
|
||||
export * from './interactions/slashCommands/SlashCommandBuilder.js';
|
||||
export * from './interactions/slashCommands/SlashCommandSubcommands.js';
|
||||
|
||||
Reference in New Issue
Block a user