Skip to content

Commit

Permalink
Adding JSDoc for better editor assistance on buttons (#3878)
Browse files Browse the repository at this point in the history
* chore: Updating the comment style so that the comment shows up in LSP.

* feat: Adding JSDoc to make usage of the component more transparent.
Including some information about accessibility concerns over variants.

* fix: Removing deprecated defaultProps in favor of destructuring.

* feat: Adding changeset information.
  • Loading branch information
cogwizzle authored May 7, 2024
1 parent f57a44d commit 1177b8f
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 21 deletions.
6 changes: 6 additions & 0 deletions .changeset/gold-boats-explain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@twilio-paste/button": patch
"@twilio-paste/core": patch
---

Adding help text to the paste button component via JSDoc to ensure the design system guidance is accessible via the Language Server output. Minor updates to the syntax of the button component to modernize it.
67 changes: 53 additions & 14 deletions packages/paste-core/components/button/src/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import type {

const AnimatedBox = animated(Box);

/*
/**
* If size isn't passed, come up with a smart default:
* - 'reset' for variant 'link'
* - 'icon' if there's 1 child that's an icon
Expand Down Expand Up @@ -57,6 +57,13 @@ const getButtonSize = (variant: ButtonVariants, children: React.ReactNode, size?
return smartSize;
};

/**
* Determine the button state based on if it is disabled or loading.
*
* @param disabled - If the button is disabled.
* @param loading - If the button is loading.
* @returns The button state.
*/
const getButtonState = (disabled?: boolean, loading?: boolean): ButtonStates => {
if (disabled) {
return "disabled";
Expand All @@ -67,6 +74,11 @@ const getButtonState = (disabled?: boolean, loading?: boolean): ButtonStates =>
return "default";
};

/**
* Validation ensuring the button is not being used in an inaccessible way.
*
* @throws Error if the button is being used in an inaccessible way.
*/
const handlePropValidation = ({
as,
href,
Expand Down Expand Up @@ -129,6 +141,9 @@ const handlePropValidation = ({

const variantsWithoutBoundingBox = new Set(["link", "destructive_link", "inverse_link", "reset"]);

/**
* Display the inner content of the button.
*/
const ButtonContents: React.FC<React.PropsWithChildren<ButtonContentsProps>> = ({
buttonState,
children,
Expand Down Expand Up @@ -173,6 +188,14 @@ const ButtonContents: React.FC<React.PropsWithChildren<ButtonContentsProps>> = (

ButtonContents.displayName = "ButtonContents";

/**
* Determine which of the button components should be used based on variant.
*
* @description This is a button factory.
*
* @param variant - The variant of the button.
* @returns The button component.
*/
const getButtonComponent = (
variant: ButtonVariants,
): React.ForwardRefExoticComponent<DirectButtonProps & React.RefAttributes<HTMLButtonElement>> => {
Expand Down Expand Up @@ -206,9 +229,25 @@ const getButtonComponent = (
};

// memo
/**
* Paste buttton component.
*
* @link [PasteButton](https://paste.twilio.design/components/button)
* @see [Accessiblity](https://paste.twilio.design/components/button#button-vs-anchor-link)
*/
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
({ element = "BUTTON", i18nExternalLinkLabel = "(link takes you to an external page)", ...props }, ref) => {
const { size, variant, children, disabled, loading, ...rest } = props;
({
element = "BUTTON",
i18nExternalLinkLabel = "(link takes you to an external page)",
as = 'button',
fullWidth = false,
disabled = false,
loading = false,
type = 'button',
variant = 'primary',
...props
}, ref) => {
const { size, children, ...rest } = props;
const [hovered, setHovered] = React.useState(false);
const arrowIconStyles = useSpring({
translateX: hovered ? "4px" : "0px",
Expand All @@ -223,7 +262,16 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
return getButtonSize(variant, children, size);
}, [size, variant, children]);

handlePropValidation({ ...props, size: smartDefaultSize });
handlePropValidation({
as,
fullWidth,
disabled,
loading,
type,
variant,
...props,
size: smartDefaultSize
});

const buttonState = getButtonState(disabled, loading);
const showLoading = buttonState === "loading";
Expand All @@ -233,7 +281,7 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(

// Automatically inject AnchorForwardIcon for link's dressed as buttons when possible
let injectIconChildren = children;
if (props.as === "a" && props.href != null && typeof children === "string" && variant !== "reset") {
if (as === "a" && props.href != null && typeof children === "string" && variant !== "reset") {
injectIconChildren = (
<>
{children}
Expand Down Expand Up @@ -280,15 +328,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
},
);

Button.defaultProps = {
as: "button",
fullWidth: false,
disabled: false,
loading: false,
type: "button",
variant: "primary",
};

Button.displayName = "Button";

export type { ButtonProps };
Expand Down
32 changes: 25 additions & 7 deletions packages/paste-core/components/button/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { BoxProps, BoxStyleProps } from "@twilio-paste/box";
import type { BoxProps } from "@twilio-paste/box";
import type { HTMLPasteProps } from "@twilio-paste/types";

type ButtonTypes = "submit" | "button" | "reset";
Expand All @@ -11,6 +11,11 @@ export type ButtonSizes =
| "rounded_small"
| "circle"
| "circle_small";
/**
* Base Button variants. [Avoid using link variants when possible.](https://paste.twilio.design/components/button#button-vs-anchor-link)
*
* @summary List of all of the differnt button appearances minus reset.
*/
type ButtonBaseVariants =
| "primary"
| "primary_icon"
Expand All @@ -24,13 +29,23 @@ type ButtonBaseVariants =
| "inverse_link"
| "inverse";
type ButtonResetVariant = "reset";
/**
* All Button variants. Includes reset. [Avoid using link variants when possible.](https://paste.twilio.design/components/button#button-vs-anchor-link)
*
* @summary List of all of the differnt button appearances.
*/
export type ButtonVariants = ButtonResetVariant | ButtonBaseVariants;
/** The various states of interactivity for the button. */
export type ButtonStates = "disabled" | "loading" | "default";
export type ButtonTabIndexes = 0 | -1;


export interface ButtonContentsProps {
/** The various states of interactivity for the button. */
buttonState: ButtonStates;
/** Is is the button in a loading state. */
showLoading: boolean;
/** Different kind of button variants */
variant?: ButtonVariants;
}

Expand All @@ -40,6 +55,7 @@ export interface DirectButtonProps extends HTMLPasteProps<"button"> {
* @default 'button'
*/
as?: keyof JSX.IntrinsicElements;
/** The various states of interactivity for the button. */
buttonState: ButtonStates;
children: React.ReactNode;
/**
Expand Down Expand Up @@ -82,16 +98,18 @@ export interface DirectButtonProps extends HTMLPasteProps<"button"> {
*/
type?: ButtonTypes;
/**
* The different appearance variants for a button. [Avoid using link variants when possible.](https://paste.twilio.design/components/button#button-vs-anchor-link)
*
* @default 'primary'
*/
variant: ButtonVariants;
}

type BaseVariantsButtonProps = {
variant?: ButtonBaseVariants;
};
type ResetVariantButtonProps = Omit<BoxStyleProps, "size"> & {
variant?: ButtonResetVariant;
type VariantsButtonProps = {
/**
* The different appearance variants for a button. [Avoid using link variants when possible.](https://paste.twilio.design/components/button#button-vs-anchor-link)
*/
variant?: ButtonBaseVariants | ButtonResetVariant;
};

export type ButtonProps = Omit<DirectButtonProps, "buttonState" | "i18nExternalLinkLabel" | "loading" | "size"> & {
Expand All @@ -109,4 +127,4 @@ export type ButtonProps = Omit<DirectButtonProps, "buttonState" | "i18nExternalL
* @default 'default'
*/
size?: ButtonSizes;
} & (BaseVariantsButtonProps | ResetVariantButtonProps);
} & (VariantsButtonProps);

0 comments on commit 1177b8f

Please sign in to comment.