From 2b690617578320300d0e6f03f84c27ed3fff8891 Mon Sep 17 00:00:00 2001 From: Christina Yang <ychristina011@gmail.com> Date: Mon, 6 Jan 2025 21:49:15 -0800 Subject: [PATCH] circled close button for alert --- src/commands/__tests__/delete.ts | 4 +- src/components/Alert.tsx | 30 ++++++------- src/components/CloseButton.tsx | 76 ++++++++++++++++++++++++++------ src/components/PopupBase.tsx | 6 +-- 4 files changed, 83 insertions(+), 33 deletions(-) diff --git a/src/commands/__tests__/delete.ts b/src/commands/__tests__/delete.ts index 82ff312c251..f70031028d1 100644 --- a/src/commands/__tests__/delete.ts +++ b/src/commands/__tests__/delete.ts @@ -35,8 +35,8 @@ describe('delete', () => { await act(vi.runAllTimersAsync) - const popupValue = await screen.findByTestId('alert')! - expect(popupValue.textContent).toBe('Permanently deleted test✕') + const popupValue = await screen.findByTestId('alert-content')! + expect(popupValue.textContent).toBe('Permanently deleted test') }) }) diff --git a/src/components/Alert.tsx b/src/components/Alert.tsx index 6ed4b490ee4..580c781ed81 100644 --- a/src/components/Alert.tsx +++ b/src/components/Alert.tsx @@ -27,8 +27,6 @@ const Alert: FC = () => { const value = strip(alertStoreValue ?? alert?.value ?? '') const iconSize = useSelector(state => 0.78 * state.fontSize) const dispatch = useDispatch() - const paddingInline = useSelector(state => (state.fontSize * 16) / 14) - const paddingBlock = paddingInline * 0.75 /** Dismiss the alert on close. */ const onClose = useCallback(() => { @@ -55,14 +53,7 @@ const Alert: FC = () => { <PopupBase disableTop cssRaw={css.raw({ - position: 'fixed', boxSizing: 'border-box', - display: 'flex', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'flex-start', - padding: '12px 16px', - gap: '8px', bottom: '36px', left: '50%', transform: 'translateX(-50%)', @@ -73,14 +64,23 @@ const Alert: FC = () => { })} ref={popupRef} key={value} - data-testid='alert-content' - onClose={onClose} - closeButtonSize='sm' + circledCloseButton showXOnHover - style={{ padding: `${paddingBlock}px ${paddingInline}px` }} > - {renderedIcon} - {value} + <div + data-testid='alert-content' + className={css({ + gap: '12px', + display: 'flex', + flexDirection: 'row', + justifyContent: 'center', + alignItems: 'flex-start', + padding: '0.85em 1.1em', + })} + > + {renderedIcon} + {value} + </div> </PopupBase> </FadeTransition> ) : null} diff --git a/src/components/CloseButton.tsx b/src/components/CloseButton.tsx index 1c98812f133..d9b0ad4ca34 100644 --- a/src/components/CloseButton.tsx +++ b/src/components/CloseButton.tsx @@ -1,23 +1,25 @@ +import { PropsWithChildren } from 'react' import { useSelector } from 'react-redux' import { css, cx } from '../../styled-system/css' import { upperRightRecipe } from '../../styled-system/recipes' import { SystemStyleObject } from '../../styled-system/types' import fastClick from '../util/fastClick' -/** A close button with an ✕. */ -const CloseButton = ({ - onClose, - disableSwipeToDismiss, - size = 'md', - cssRaw, -}: { +type CloseButtonProps = { onClose: () => void disableSwipeToDismiss?: boolean - size?: 'sm' | 'md' cssRaw?: SystemStyleObject -}) => { - const fontSize = useSelector(state => state.fontSize) - const padding = (fontSize / 2 + 2) / (size === 'sm' ? 2 : 1) + style?: React.CSSProperties +} + +/** Base for a close button. */ +const BaseCloseButton = ({ + onClose, + disableSwipeToDismiss, + cssRaw, + style, + children, +}: PropsWithChildren<CloseButtonProps>) => { return ( <a {...fastClick(onClose)} @@ -33,14 +35,62 @@ const CloseButton = ({ cssRaw, ), )} - style={{ fontSize: size === 'sm' ? fontSize / 2 : fontSize, padding: `${padding}px ${padding * 1.25}px` }} + style={style} aria-label={disableSwipeToDismiss ? 'no-swipe-to-dismiss' : undefined} data-testid='close-button' data-close-button > - ✕ + {children} </a> ) } +/** A circled close button with an ✕. Sized using parent fontsize. */ +const CircledCloseButton = ({ cssRaw, ...props }: CloseButtonProps) => { + return ( + <BaseCloseButton + {...props} + cssRaw={css.raw( + { + display: 'flex', + // inherit not yet supported by plugin + // eslint-disable-next-line @pandacss/no-hardcoded-color + color: 'inherit', + textDecoration: 'none', + borderRadius: '50%', + transform: 'translate(50%, -50%)', + // inherit not yet supported by plugin + // eslint-disable-next-line @pandacss/no-hardcoded-color + background: 'inherit', + border: 'inherit', + }, + cssRaw, + )} + > + <svg width='1em' height='1em' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'> + <path + d='M4.49642 12.1284L8.06394 8.56023L11.5039 12.0166L12.0802 11.4566L8.62388 8.00023L12.0802 4.54391L11.5039 3.98391L8.06394 7.44023L4.49642 3.87207L3.92017 4.43207L7.48833 8.00023L3.92017 11.5684L4.49642 12.1284Z' + fill='white' + /> + </svg> + </BaseCloseButton> + ) +} + +/** A close button with an ✕. */ +const LetterCloseButton = (props: CloseButtonProps) => { + const fontSize = useSelector(state => state.fontSize) + const padding = fontSize / 2 + 2 + return ( + <BaseCloseButton {...props} style={{ fontSize, padding: `${padding}px ${padding * 1.25}px` }}> + ✕ + </BaseCloseButton> + ) +} + +/** A close button with an ✕. */ +const CloseButton = ({ circled, ...props }: CloseButtonProps & { circled?: boolean }) => { + return circled ? <CircledCloseButton {...props} /> : <LetterCloseButton {...props} /> +} + export default CloseButton diff --git a/src/components/PopupBase.tsx b/src/components/PopupBase.tsx index 9a75cd9a0d2..294c0727cbe 100644 --- a/src/components/PopupBase.tsx +++ b/src/components/PopupBase.tsx @@ -22,7 +22,7 @@ export type PopupBaseProps = PropsWithChildren< onClose?: () => void disableTop?: boolean cssRaw?: SystemStyleObject - closeButtonSize?: 'sm' | 'md' + circledCloseButton?: boolean showXOnHover?: boolean } & Omit<React.HTMLAttributes<HTMLDivElement>, 'className'> > @@ -30,7 +30,7 @@ export type PopupBaseProps = PropsWithChildren< /** A popup component that can be dismissed. */ const PopupBase = React.forwardRef<HTMLDivElement, PopupBaseProps>( ( - { children, importFileId, onClose, cssRaw, style, disableTop = false, closeButtonSize, showXOnHover, ...props }, + { children, importFileId, onClose, cssRaw, style, disableTop = false, circledCloseButton, showXOnHover, ...props }, ref, ) => { const dispatch = useDispatch() @@ -94,8 +94,8 @@ const PopupBase = React.forwardRef<HTMLDivElement, PopupBaseProps>( )} {onClose ? ( <CloseButton + circled={circledCloseButton} cssRaw={showXOnHover ? { visibility: 'hidden' } : undefined} - size={closeButtonSize} onClose={onClose} disableSwipeToDismiss />