Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding JSDoc to Paste Components #3902

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export interface AccountSwitcherProps extends MenuProps {
element?: BoxProps["element"];
}

/**
* An Account Switcher component is a stylized Menu Badge with a list of actions related to a user's accounts.
*
* @link [Account Switcher](https://paste.twilio.design/components/account-switcher)
*/
const AccountSwitcher = React.forwardRef<HTMLDivElement, AccountSwitcherProps>(
({ children, element = "ACCOUNT_SWITCHER", ...props }, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export interface AccountSwitcherBadgeProps extends Omit<MenuBadgeProps, "variant
element?: BoxProps["element"];
}

/**
* Used to display the name of the current account selected or being viewed, and contains the menu trigger. The text inside the badge should update when a new account is selected.
*
* @link [Account Switcher Badge](https://paste.twilio.design/components/account-switcher#account-switcher-badge)
*/
const AccountSwitcherBadge = React.forwardRef<HTMLButtonElement, AccountSwitcherBadgeProps>(
({ children, element = "ACCOUNT_SWITCHER_BADGE", ...props }, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export interface AccountSwitcherGroupProps extends MenuGroupProps {
element?: BoxProps["element"];
}

/**
* Used to group similar items together in the Account Switcher menu. An example of this might be a list of recent accounts.
*
* @link [Account Switcher Group](https://paste.twilio.design/components/account-switcher#account-switcher-group)
*/
const AccountSwitcherGroup = React.forwardRef<HTMLDivElement, AccountSwitcherGroupProps>(
({ children, element = "ACCOUNT_SWITCHER_GROUP", label, ...props }, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export interface AccountSwitcherItemProps extends MenuItemProps {
element?: BoxProps["element"];
}

/**
* A menu item that can either perform an action or navigate to a new URL.
*
* @link [Account Switcher Item](https://paste.twilio.design/components/account-switcher#account-switcher-item)
*/
const AccountSwitcherItem = React.forwardRef<HTMLDivElement, AccountSwitcherItemProps>(
({ children, element = "ACCOUNT_SWITCHER_ITEM", ...props }, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export interface AccountSwitcherItemRadioProps extends MenuItemRadioProps {
element?: BoxProps["element"];
}

/**
* A menu item that can perform a single selection of an item within a named group. Similar to a radio button group, only one item can be selected at a time. Each item in the group should have a name and value and must be contained in a Group.
*
* @link [Account Switcher Item Radio](https://paste.twilio.design/components/account-switcher#account-switcher-itemradio)
*/
const AccountSwitcherItemRadio = React.forwardRef<HTMLDivElement, AccountSwitcherItemRadioProps>(
({ children, element = "ACCOUNT_SWITCHER_ITEM_RADIO", ...props }, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ export interface AccountSwitcherSeparatorProps extends MenuSeparatorProps {
element?: BoxProps["element"];
}

/**
* Simple horizontal rule used to separate groups or individual items.
*
* @link [Account Switcher Separator](https://paste.twilio.design/components/account-switcher#account-switcher-separator)
*/
const AccountSwitcherSeparator = React.forwardRef<HTMLHRElement, AccountSwitcherSeparatorProps>(
({ children, element = "ACCOUNT_SWITCHER_SEPARATOR", ...props }, ref) => {
return (
Expand Down
123 changes: 79 additions & 44 deletions packages/paste-core/components/alert-dialog/src/AlertDialog.tsx
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review this file carefully to ensure no regressions were introduced please.

Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useTransition } from "@twilio-paste/animation-library";
import { Box, safelySpreadBoxProps } from "@twilio-paste/box";
import type { BoxProps } from "@twilio-paste/box";
import { ModalDialogOverlay } from "@twilio-paste/modal";
import type { HTMLPasteProps } from "@twilio-paste/types";
import type { SpringValue } from "@react-spring/web";
import { Box, safelySpreadBoxProps } from "@twilio-paste/box";
import { ModalDialogOverlay } from "@twilio-paste/modal";
import { useTransition } from "@twilio-paste/animation-library";
import { useUID } from "@twilio-paste/uid-library";
import * as React from "react";

Expand All @@ -11,8 +12,10 @@ import { AlertDialogContent } from "./AlertDialogContent";
import { AlertDialogFooter } from "./AlertDialogFooter";
import { AlertDialogHeader } from "./AlertDialogHeader";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
const getAnimationStates = (): any => ({
/**
* Animation states for the Alert Dialog.
*/
const AnimationStates = {
from: { opacity: 0, transform: `scale(0.675)` },
Comment on lines +15 to +18
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we were attempting to avoid using the ReactSpring types here. I replaced the function with a hard coded object so that we weren't creating new objects each time this component ran. Further down in the component I converted from the SpringValue type to the native type so we don't have to use any.

enter: { opacity: 1, transform: `scale(1)` },
leave: { opacity: 0, transform: `scale(0.675)` },
Expand All @@ -22,7 +25,7 @@ const getAnimationStates = (): any => ({
tension: 370,
friction: 26,
},
});
};

export interface AlertDialogProps extends HTMLPasteProps<"div"> {
children: NonNullable<React.ReactNode>;
Expand Down Expand Up @@ -92,6 +95,35 @@ export interface AlertDialogProps extends HTMLPasteProps<"div"> {
element?: BoxProps["element"];
}

interface NormalizeStylesArg {
opacity: SpringValue<number>;
transform: SpringValue<string>;
}

interface NormalizeStylesReturn {
opacity: number;
transform: string;
}

/**
* Normalize ReactSpring styles to be used in the AlertDialog.
*
* @param {NormalizeStylesArg} styles - ReactSpring styles
* @returns {NormalizeStylesReturn} - Normalized styles
*/
const normalizeStyles = (styles: NormalizeStylesArg): NormalizeStylesReturn => {
return {
...styles,
opacity: styles.opacity.get(),
transform: styles.transform.get()
};
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function converts our SpringValue to the native generic value of the SpringValue. This normalizes the styles and allows us to avoid the any above.

/**
* An Alert Dialog is a page overlay that displays critical information, blocks interaction with the page, and only closes after an action is performed.
*
* @link [Alert Dialog](https://paste.twilio.design/components/alert-dialog)
*/
export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
(
{
Expand All @@ -109,47 +141,50 @@ export const AlertDialog = React.forwardRef<HTMLDivElement, AlertDialogProps>(
},
ref,
) => {
const transitions = useTransition(isOpen, getAnimationStates());
const transitions = useTransition(isOpen, AnimationStates);
const headingID = useUID();
const bodyID = useUID();

return (
<>
{transitions(
(styles, item) =>
Comment on lines -116 to -118
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't need a fragment wrapping this so I removed some of the code wrapping this.

item && (
<ModalDialogOverlay isOpen={isOpen} style={{ opacity: styles.opacity }}>
<Box
// @ts-expect-error Render overlay as box for customization
as={AlertDialogContent}
{...safelySpreadBoxProps(props)}
aria-labelledby={headingID}
aria-describedby={bodyID}
element={element}
ref={ref}
role="alertdialog"
style={styles}
>
<AlertDialogHeader headingID={headingID} element={`${element}_HEADER`}>
{heading}
</AlertDialogHeader>
<AlertDialogBody bodyID={bodyID} element={`${element}_BODY`}>
{children}
</AlertDialogBody>
<AlertDialogFooter
destructive={destructive}
element={`${element}_FOOTER`}
onDismiss={onDismiss}
onDismissLabel={onDismissLabel}
onConfirm={onConfirm}
onConfirmLabel={onConfirmLabel}
onConfirmDisabled={onConfirmDisabled}
/>
</Box>
</ModalDialogOverlay>
),
)}
</>
return transitions(
(rawStyles, item) => {
if (!item) {
return null;
}
// Normalizing ReactSpring styles.
const styles = normalizeStyles(rawStyles);

return (
<ModalDialogOverlay isOpen={isOpen} style={{ opacity: styles.opacity }}>
<Box
// @ts-expect-error Render overlay as box for customization
as={AlertDialogContent}
{...safelySpreadBoxProps(props)}
aria-labelledby={headingID}
aria-describedby={bodyID}
element={element}
ref={ref}
role="alertdialog"
style={styles}
>
<AlertDialogHeader headingID={headingID} element={`${element}_HEADER`}>
{heading}
</AlertDialogHeader>
<AlertDialogBody bodyID={bodyID} element={`${element}_BODY`}>
{children}
</AlertDialogBody>
<AlertDialogFooter
destructive={destructive}
element={`${element}_FOOTER`}
onDismiss={onDismiss}
onDismissLabel={onDismissLabel}
onConfirm={onConfirm}
onConfirmLabel={onConfirmLabel}
onConfirmDisabled={onConfirmDisabled}
/>
</Box>
</ModalDialogOverlay>
);
}
);
},
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export interface AlertDialogBodyProps extends HTMLPasteProps<"div">, Pick<BoxPro
children: NonNullable<React.ReactNode>;
}

/**
* Internal body component for the AlertDialog component.
*
* @private
*/
export const AlertDialogBody = React.forwardRef<HTMLDivElement, AlertDialogBodyProps>(
({ bodyID, children, element = "ALERT_DIALOG_BODY", ...props }, ref) => {
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@ import { css, styled } from "@twilio-paste/styling-library";

export type AlertDialogContentProps = ModalDialogContentProps;

const AlertDialogContent = styled(ModalDialogContent)<AlertDialogContentProps>(() =>
/**
* Content area for the AlertDialog component.
*
* @private
*/
export const AlertDialogContent = styled(ModalDialogContent)<AlertDialogContentProps>(() =>
css({
maxWidth: "size40",
}),
);

AlertDialogContent.displayName = "AlertDialogContent";

export { AlertDialogContent };
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,25 @@ import type { HTMLPasteProps } from "@twilio-paste/types";
import * as React from "react";

export interface AlertDialogFooterProps extends HTMLPasteProps<"div">, Pick<BoxProps, "element"> {
/** Determines if the Alert Dialog is destructive. _Only changes the button color of confirm button._ */
destructive?: boolean;
/** Function to run on confirmation of the Alert Dialog. */
onConfirm: () => void;
/** Text of the confirm button. */
onConfirmLabel: string;
/** Function to run on dismiss of the Alert Dialog. */
onDismiss: () => void;
/** Text of the dismiss button. */
onDismissLabel: string;
/** Property to disable the confirm button. _Has no effect if destructive is not true._ */
onConfirmDisabled?: boolean;
Copy link
Collaborator Author

@cogwizzle cogwizzle May 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made this comment in the discussions, but I think we should rename this internal components property to isConfirmDisabled to better communicate that it is a boolean. I would not make this change as part of this PR.

}

/**
* Alert Dialog footer component for the AlertDialog component.
*
* @private
*/
export const AlertDialogFooter = React.forwardRef<HTMLDivElement, AlertDialogFooterProps>(
(
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,15 @@ import * as React from "react";

export interface AlertDialogHeaderProps extends HTMLPasteProps<"header">, Pick<BoxProps, "element"> {
children: string;
/** ID for the internal heading element. */
headingID: string;
}

/**
* Internal alert dialog header component for the AlertDialog component.
*
* @private
*/
export const AlertDialogHeader = React.forwardRef<HTMLHeadElement, AlertDialogHeaderProps>(
({ children, element = "ALERT_DIALOG_HEADER", headingID, ...props }, ref) => {
return (
Expand Down
28 changes: 26 additions & 2 deletions packages/paste-core/components/alert/src/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,28 @@ import { ScreenReaderOnly } from "@twilio-paste/screen-reader-only";
import type { HTMLPasteProps, ValueOf } from "@twilio-paste/types";
import * as React from "react";

/** Alert component variants keys. */
type AlertVariantKeys = "ERROR" | "NEUTRAL" | "WARNING";

/** Alert component roles. */
export const AlertRoles = {
ERROR: "alert",
NEUTRAL: "status",
WARNING: "alert",
} as const;
/** Alert component variants. */
export const AlertVariants = {
ERROR: "error",
NEUTRAL: "neutral",
WARNING: "warning",
} as const;
/** Alert component background colors. */
export const AlertBackgroundColors = {
ERROR: "colorBackgroundErrorWeakest",
NEUTRAL: "colorBackgroundNeutralWeakest",
WARNING: "colorBackgroundWarningWeakest",
} as const;
/** Alert component text colors. */
export const AlertTextColors = {
ERROR: "colorTextError",
NEUTRAL: "colorTextNeutral",
Expand Down Expand Up @@ -107,7 +112,20 @@ export interface AlertProps extends HTMLPasteProps<"div"> {
element?: BoxProps["element"];
}

const renderAlertIcon = (variant: AlertVariants, element: string, title: string): React.ReactElement => {
interface AlertIconProps {
variant: AlertVariants;
element: string;
title: string;
}

/**
* Component to display the appropriate alert icon.
*/
const AlertIcon: React.FC<AlertIconProps> = ({
variant,
element,
title,
}): React.ReactElement => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored this to be a component because it is a function that takes in args and returns JSX. It effectively had a signature similar to a React component.

switch (variant) {
case AlertVariants.ERROR:
return (
Expand Down Expand Up @@ -142,7 +160,13 @@ const renderAlertIcon = (variant: AlertVariants, element: string, title: string)
);
}
};
AlertIcon.displayName = "AlertIcon"

/**
* An Alert is a banner that notifies users to high-priority or time-sensitive information.
*
* @link [Alert](https://paste.twilio.design/components/alert)
*/
const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
(
{
Expand Down Expand Up @@ -180,7 +204,7 @@ const Alert = React.forwardRef<HTMLDivElement, AlertProps>(
>
<MediaObject as="div">
<MediaFigure as="div" spacing="space30">
{renderAlertIcon(variant, element, i18nLabelVariantMap[variant])}
<AlertIcon variant={variant} element={element} title={i18nLabelVariantMap[variant]} />
</MediaFigure>
<MediaBody as="div">
<Box color={AlertTextColors[variant.toUpperCase() as AlertVariantKeys]}>{children}</Box>
Expand Down
Loading
Loading