Skip to content

Commit

Permalink
feat(components): Add unsafe message component builders (#7387)
Browse files Browse the repository at this point in the history
  • Loading branch information
suneettipirneni authored Feb 4, 2022
1 parent 04502ce commit 6b6222b
Show file tree
Hide file tree
Showing 12 changed files with 566 additions and 430 deletions.
2 changes: 1 addition & 1 deletion packages/builders/__tests__/components/button.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
ComponentType,
} from 'discord-api-types/v9';
import { buttonLabelValidator, buttonStyleValidator } from '../../src/components/Assertions';
import { ButtonComponent } from '../../src/components/Button';
import { ButtonComponent } from '../../src/components/button/Button';

const buttonComponent = () => new ButtonComponent();

Expand Down
42 changes: 42 additions & 0 deletions packages/builders/src/components/button/Button.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import type { ButtonStyle, APIMessageComponentEmoji, APIButtonComponent } from 'discord-api-types/v9';
import {
buttonLabelValidator,
buttonStyleValidator,
customIdValidator,
disabledValidator,
emojiValidator,
urlValidator,
validateRequiredButtonParameters,
} from '../Assertions';
import { UnsafeButtonComponent } from './UnsafeButton';

export class ButtonComponent extends UnsafeButtonComponent {
public override setStyle(style: ButtonStyle) {
return super.setStyle(buttonStyleValidator.parse(style));
}

public override setURL(url: string) {
return super.setURL(urlValidator.parse(url));
}

public override setCustomId(customId: string) {
return super.setCustomId(customIdValidator.parse(customId));
}

public override setEmoji(emoji: APIMessageComponentEmoji) {
return super.setEmoji(emojiValidator.parse(emoji));
}

public override setDisabled(disabled: boolean) {
return super.setDisabled(disabledValidator.parse(disabled));
}

public override setLabel(label: string) {
return super.setLabel(buttonLabelValidator.parse(label));
}

public override toJSON(): APIButtonComponent {
validateRequiredButtonParameters(this.style, this.label, this.emoji, this.custom_id, this.url);
return super.toJSON();
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,12 @@
import { APIButtonComponent, APIMessageComponentEmoji, ButtonStyle, ComponentType } from 'discord-api-types/v9';
import {
buttonLabelValidator,
buttonStyleValidator,
customIdValidator,
disabledValidator,
emojiValidator,
urlValidator,
validateRequiredButtonParameters,
} from './Assertions';
import type { Component } from './Component';
ComponentType,
ButtonStyle,
type APIMessageComponentEmoji,
type APIButtonComponent,
} from 'discord-api-types/v9';
import type { Component } from '../Component';

export class ButtonComponent implements Component {
export class UnsafeButtonComponent implements Component {
public readonly type = ComponentType.Button as const;
public readonly style!: ButtonStyle;
public readonly label?: string;
Expand Down Expand Up @@ -41,7 +37,6 @@ export class ButtonComponent implements Component {
* @param style The style of the button
*/
public setStyle(style: ButtonStyle) {
buttonStyleValidator.parse(style);
Reflect.set(this, 'style', style);
return this;
}
Expand All @@ -51,7 +46,6 @@ export class ButtonComponent implements Component {
* @param url The URL to open when this button is clicked
*/
public setURL(url: string) {
urlValidator.parse(url);
Reflect.set(this, 'url', url);
return this;
}
Expand All @@ -61,7 +55,6 @@ export class ButtonComponent implements Component {
* @param customId The custom ID to use for this button
*/
public setCustomId(customId: string) {
customIdValidator.parse(customId);
Reflect.set(this, 'custom_id', customId);
return this;
}
Expand All @@ -71,7 +64,6 @@ export class ButtonComponent implements Component {
* @param emoji The emoji to display on this button
*/
public setEmoji(emoji: APIMessageComponentEmoji) {
emojiValidator.parse(emoji);
Reflect.set(this, 'emoji', emoji);
return this;
}
Expand All @@ -81,7 +73,6 @@ export class ButtonComponent implements Component {
* @param disabled Whether or not to disable this button or not
*/
public setDisabled(disabled: boolean) {
disabledValidator.parse(disabled);
Reflect.set(this, 'disabled', disabled);
return this;
}
Expand All @@ -91,13 +82,11 @@ export class ButtonComponent implements Component {
* @param label The label to display on this button
*/
public setLabel(label: string) {
buttonLabelValidator.parse(label);
Reflect.set(this, 'label', label);
return this;
}

public toJSON(): APIButtonComponent {
validateRequiredButtonParameters(this.style, this.label, this.emoji, this.custom_id, this.url);
return {
...this,
};
Expand Down
102 changes: 15 additions & 87 deletions packages/builders/src/components/selectMenu/SelectMenu.ts
Original file line number Diff line number Diff line change
@@ -1,111 +1,39 @@
import { APISelectMenuComponent, ComponentType } from 'discord-api-types/v9';
import type { APISelectMenuComponent } from 'discord-api-types/v9';
import {
customIdValidator,
disabledValidator,
minMaxValidator,
placeholderValidator,
validateRequiredSelectMenuParameters,
} from '../Assertions';
import type { Component } from '../Component';
import { SelectMenuOption } from './SelectMenuOption';
import { UnsafeSelectMenuComponent } from './UnsafeSelectMenu';

/**
* Represents a select menu component
*/
export class SelectMenuComponent implements Component {
public readonly type = ComponentType.SelectMenu as const;
public readonly options: SelectMenuOption[];
public readonly placeholder?: string;
public readonly min_values?: number;
public readonly max_values?: number;
public readonly custom_id!: string;
public readonly disabled?: boolean;

public constructor(data?: APISelectMenuComponent & { type?: ComponentType.SelectMenu }) {
this.options = data?.options.map((option) => new SelectMenuOption(option)) ?? [];
this.placeholder = data?.placeholder;
this.min_values = data?.min_values;
this.max_values = data?.max_values;
/* eslint-disable @typescript-eslint/non-nullable-type-assertion-style */
this.custom_id = data?.custom_id as string;
/* eslint-enable @typescript-eslint/non-nullable-type-assertion-style */
this.disabled = data?.disabled;
}

/**
* Sets the placeholder for this select menu
* @param placeholder The placeholder to use for this select menu
*/
public setPlaceholder(placeholder: string) {
placeholderValidator.parse(placeholder);
Reflect.set(this, 'placeholder', placeholder);
return this;
}

/**
* Sets the minimum values that must be selected in the select menu
* @param minValues The minimum values that must be selected
*/
public setMinValues(minValues: number) {
minMaxValidator.parse(minValues);
Reflect.set(this, 'min_values', minValues);
return this;
}

/**
* Sets the maximum values that must be selected in the select menu
* @param minValues The maximum values that must be selected
*/
public setMaxValues(maxValues: number) {
minMaxValidator.parse(maxValues);
Reflect.set(this, 'max_values', maxValues);
return this;
export class SelectMenuComponent extends UnsafeSelectMenuComponent {
public override setPlaceholder(placeholder: string) {
return super.setPlaceholder(placeholderValidator.parse(placeholder));
}

/**
* Sets the custom Id for this select menu
* @param customId The custom ID to use for this select menu
*/
public setCustomId(customId: string) {
customIdValidator.parse(customId);
Reflect.set(this, 'custom_id', customId);
return this;
public override setMinValues(minValues: number) {
return super.setMinValues(minMaxValidator.parse(minValues));
}

/**
* Sets whether or not this select menu is disabled
* @param disabled Whether or not this select menu is disabled
*/
public setDisabled(disabled: boolean) {
disabledValidator.parse(disabled);
Reflect.set(this, 'disabled', disabled);
return this;
public override setMaxValues(maxValues: number) {
return super.setMaxValues(minMaxValidator.parse(maxValues));
}

/**
* Adds options to this select menu
* @param options The options to add to this select menu
* @returns
*/
public addOptions(...options: SelectMenuOption[]) {
this.options.push(...options);
return this;
public override setCustomId(customId: string) {
return super.setCustomId(customIdValidator.parse(customId));
}

/**
* Sets the options on this select menu
* @param options The options to set on this select menu
*/
public setOptions(options: SelectMenuOption[]) {
Reflect.set(this, 'options', [...options]);
return this;
public override setDisabled(disabled: boolean) {
return super.setDisabled(disabledValidator.parse(disabled));
}

public toJSON(): APISelectMenuComponent {
public override toJSON(): APISelectMenuComponent {
validateRequiredSelectMenuParameters(this.options, this.custom_id);
return {
...this,
options: this.options.map((option) => option.toJSON()),
};
return super.toJSON();
}
}
73 changes: 10 additions & 63 deletions packages/builders/src/components/selectMenu/SelectMenuOption.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,79 +5,26 @@ import {
labelValueValidator,
validateRequiredSelectMenuOptionParameters,
} from '../Assertions';
import { UnsafeSelectMenuOption } from './UnsafeSelectMenuOption';

/**
* Represents an option within a select menu component
*/
export class SelectMenuOption {
public readonly label!: string;
public readonly value!: string;
public readonly description?: string;
public readonly emoji?: APIMessageComponentEmoji;
public readonly default?: boolean;

public constructor(data?: APISelectMenuOption) {
/* eslint-disable @typescript-eslint/non-nullable-type-assertion-style */
this.label = data?.label as string;
this.value = data?.value as string;
/* eslint-enable @typescript-eslint/non-nullable-type-assertion-style */
this.description = data?.description;
this.emoji = data?.emoji;
this.default = data?.default;
}

/**
* Sets the label of this option
* @param label The label to show on this option
*/
public setLabel(label: string) {
Reflect.set(this, 'label', label);
return this;
}

/**
* Sets the value of this option
* @param value The value of this option
*/
public setValue(value: string) {
Reflect.set(this, 'value', value);
return this;
}

/**
* Sets the description of this option.
* @param description The description of this option
*/
public setDescription(description: string) {
labelValueValidator.parse(description);
Reflect.set(this, 'description', description);
return this;
export class SelectMenuOption extends UnsafeSelectMenuOption {
public override setDescription(description: string) {
return super.setDescription(labelValueValidator.parse(description));
}

/**
* Sets whether this option is selected by default
* @param isDefault Whether or not this option is selected by default
*/
public setDefault(isDefault: boolean) {
defaultValidator.parse(isDefault);
Reflect.set(this, 'default', isDefault);
return this;
public override setDefault(isDefault: boolean) {
return super.setDefault(defaultValidator.parse(isDefault));
}

/**
* Sets the emoji to display on this button
* @param emoji The emoji to display on this button
*/
public setEmoji(emoji: APIMessageComponentEmoji) {
emojiValidator.parse(emoji);
Reflect.set(this, 'emoji', emoji);
return this;
public override setEmoji(emoji: APIMessageComponentEmoji) {
return super.setEmoji(emojiValidator.parse(emoji));
}

public toJSON(): APISelectMenuOption {
public override toJSON(): APISelectMenuOption {
validateRequiredSelectMenuOptionParameters(this.label, this.value);
return {
...this,
};
return super.toJSON();
}
}
Loading

0 comments on commit 6b6222b

Please sign in to comment.