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

Convert the Snackbar component to TypeScript #42648

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
@@ -1,34 +1,35 @@
/**
* External dependencies
*/
import type { ForwardedRef, MouseEvent, KeyboardEvent } from 'react';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { speak } from '@wordpress/a11y';
import { useEffect, forwardRef, renderToString } from '@wordpress/element';
import type { WPElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import warning from '@wordpress/warning';

/**
* Internal dependencies
*/
import { Button } from '../';
import { Button } from '../button';
import type { SnackbarProps } from './types';

const noop = () => {};
const NOTICE_TIMEOUT = 10000;

/** @typedef {import('@wordpress/element').WPElement} WPElement */

/**
* Custom hook which announces the message with the given politeness, if a
* valid message is provided.
*
* @param {string|WPElement} [message] Message to announce.
* @param {'polite'|'assertive'} politeness Politeness to announce.
*/
function useSpokenMessage( message, politeness ) {
function useSpokenMessage(
message: string | WPElement,
politeness: 'polite' | 'assertive'
) {
const spokenMessage =
typeof message === 'string' ? message : renderToString( message );

Expand All @@ -54,24 +55,28 @@ function Snackbar(
// actually the function to call to remove the snackbar from the UI.
onDismiss = noop,
listRef,
},
ref
}: SnackbarProps,
ref: ForwardedRef< any >
) {
onDismiss = onDismiss || noop;

function dismissMe( event ) {
function dismissMe(
event:
| KeyboardEvent< HTMLDivElement | HTMLSpanElement >
| MouseEvent< HTMLDivElement | HTMLSpanElement >
) {
if ( event && event.preventDefault ) {
event.preventDefault();
}

// Prevent focus loss by moving it to the list element.
listRef.current.focus();
listRef?.current?.focus();

onDismiss();
onRemove();
}

function onActionClick( event, onClick ) {
function onActionClick( event: Event, onClick?: ( event: Event ) => {} ) {
event.stopPropagation();

onRemove();
Expand Down Expand Up @@ -119,7 +124,7 @@ function Snackbar(
ref={ ref }
className={ classes }
onClick={ ! explicitDismiss ? dismissMe : noop }
tabIndex="0"
tabIndex={ 0 }
role={ ! explicitDismiss ? 'button' : '' }
onKeyPress={ ! explicitDismiss ? dismissMe : noop }
aria-label={ ! explicitDismiss ? __( 'Dismiss this notice' ) : '' }
Expand All @@ -135,7 +140,7 @@ function Snackbar(
key={ index }
href={ url }
variant="tertiary"
onClick={ ( event ) =>
onClick={ ( event: Event ) =>
onActionClick( event, onClick )
}
className="components-snackbar__action"
Expand All @@ -148,7 +153,7 @@ function Snackbar(
<span
role="button"
aria-label="Dismiss this notice"
tabIndex="0"
tabIndex={ 0 }
className="components-snackbar__dismiss-button"
onClick={ dismissMe }
onKeyPress={ dismissMe }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* External dependencies
*/
import type { MutableRefObject } from 'react';
import classnames from 'classnames';
import { omit } from 'lodash';

Expand All @@ -13,11 +14,12 @@ import { useRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import Snackbar from './';
import Snackbar from '.';
import {
__unstableMotion as motion,
__unstableAnimatePresence as AnimatePresence,
} from '../animation';
import type { Notice, SnackbarListProps } from './types';

const noop = () => {};
const SNACKBAR_VARIANTS = {
Expand All @@ -41,27 +43,21 @@ const SNACKBAR_VARIANTS = {
};

const SNACKBAR_REDUCE_MOTION_VARIANTS = {
init: false,
open: false,
exit: false,
init: {},
open: {},
exit: {},
};

/**
* Renders a list of notices.
*
* @param {Object} $0 Props passed to the component.
* @param {Array} $0.notices Array of notices to render.
* @param {Function} $0.onRemove Function called when a notice should be removed / dismissed.
* @param {Object} $0.className Name of the class used by the component.
* @param {Object} $0.children Array of children to be rendered inside the notice list.
*
* @return {Object} The rendered notices list.
*/
function SnackbarList( { notices, className, children, onRemove = noop } ) {
const listRef = useRef();
function SnackbarList( {
notices,
className,
children,
onRemove = noop,
}: SnackbarListProps ) {
const listRef = useRef() as MutableRefObject< HTMLDivElement >;
const isReducedMotion = useReducedMotion();
className = classnames( 'components-snackbar-list', className );
const removeNotice = ( notice ) => () => onRemove( notice.id );
const removeNotice = ( notice: Notice ) => () => onRemove( notice.id );
return (
<div className={ className } tabIndex={ -1 } ref={ listRef }>
{ children }
Expand Down
49 changes: 49 additions & 0 deletions packages/components/src/snackbar/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* External dependencies
*/
import type { MutableRefObject } from 'react';

/**
* WordPress dependencies
*/
import type { WPElement } from '@wordpress/element';

export type Action = {
label: string;
url: string;
onClick?: ( event: Event ) => {};
};

export type SnackbarProps = {
className?: string;
children: string;
spokenMessage?: string | WPElement;
politeness?: 'polite' | 'assertive';
actions?: Array< Action >;
onRemove?: Function;
icon?: WPElement | null;
explicitDismiss?: boolean;
onDismiss?: Function;
listRef?: MutableRefObject< HTMLDivElement >;
};

export type Notice = {
id: string;
status: string;
content: string;
spokenMessage: string | WPElement;
__unstableHTML?: string;
isDismissible: boolean;
actions: Array< Action >;
type: string;
icon?: WPElement;
explicitDismiss: boolean;
onDismiss: Function;
};

export type SnackbarListProps = {
notices: Array< Notice >;
children?: Array< string >;
onRemove?: Function;
className?: string;
};
2 changes: 2 additions & 0 deletions packages/components/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"include": [
"src/__next/**/*",
"src/animate/**/*",
"src/animation/**/*",
"src/base-control/**/*",
"src/base-field/**/*",
"src/border-box-control/**/*",
Expand Down Expand Up @@ -81,6 +82,7 @@
"src/select-control/**/*",
"src/shortcut/**/*",
"src/slot-fill/**/*",
"src/snackbar/**/*",
"src/style-provider/**/*",
"src/spacer/**/*",
"src/spinner/**/*",
Expand Down