diff --git a/packages/components/CHANGELOG.md b/packages/components/CHANGELOG.md index fe144c59ec1bb1..fc7bda7aabe57f 100644 --- a/packages/components/CHANGELOG.md +++ b/packages/components/CHANGELOG.md @@ -29,6 +29,7 @@ - `MenuGroup`: Convert to TypeScript ([#45617](https://github.com/WordPress/gutenberg/pull/45617)). - `useCx`: fix story to satisfy the `react-hooks/exhaustive-deps` eslint rule ([#45614](https://github.com/WordPress/gutenberg/pull/45614)) - Activate the `react-hooks/exhuastive-deps` eslint rule for the Components package ([#41166](https://github.com/WordPress/gutenberg/pull/41166)) +- `Snackbar`: Convert to TypeScript ([#45472](https://github.com/WordPress/gutenberg/pull/45472)). ### Experimental diff --git a/packages/components/src/snackbar/README.md b/packages/components/src/snackbar/README.md index e0fc4ea09b1a80..bbff9b9efabcb6 100644 --- a/packages/components/src/snackbar/README.md +++ b/packages/components/src/snackbar/README.md @@ -36,17 +36,72 @@ const MySnackbarNotice = () => ( ); ``` -#### Props +### Props The following props are used to control the display of the component. -- `children`: (string) The displayed message of a notice. Also used as the spoken message for assistive technology, unless `spokenMessage` is provided as an alternative message. -- `spokenMessage`: (string) Used to provide a custom spoken message in place of the `children` default. -- `politeness`: (string) A politeness level for the notice's spoken message. Should be provided as one of the valid options for [an `aria-live` attribute value](https://www.w3.org/TR/wai-aria-1.1/#aria-live). Defaults to `"polite"`. Note that this value should be considered a suggestion; assistive technologies may override it based on internal heuristics. - - A value of `'assertive'` is to be used for important, and usually time-sensitive, information. It will interrupt anything else the screen reader is announcing in that moment. - - A value of `'polite'` is to be used for advisory information. It should not interrupt what the screen reader is announcing in that moment (the "speech queue") or interrupt the current task. -- `onRemove`: function called when dismissing the notice. -- `actions`: (array) an array of action objects. Each member object should contain a `label` and either a `url` link string or `onClick` callback function. +#### `actions`: `NoticeAction[]` + +An array of action objects. Each member object should contain a `label` and either a `url` link string or `onClick` callback function. + +- Required: No +- Default: `[]` + +#### `children`: `string` + +The displayed message of a notice. Also used as the spoken message for assistive technology, unless `spokenMessage` is provided as an alternative message. + +- Required: Yes + +#### `explicitDismiss`: `boolean` + +Whether to require user action to dismiss the snackbar. By default, this is dismissed on a timeout, without user interaction. + +- Required: No +- Default: `false` + +#### `icon`: `ReactNode` + +The icon to render in the snackbar. + +- Required: No +- Default: `null` + +#### `listRef`: `MutableRefObject< HTMLDivElement | null >` + +A ref to the list that contains the snackbar. + +- Required: No + +#### `onDismiss`: `() => void` + +A callback executed when the snackbar is dismissed. It is distinct from onRemove, which _looks_ like a callback but is actually the function to call to remove the snackbar from the UI. + +- Required: No + +#### `onRemove`: `() => void` + +Function called when dismissing the notice. + +- Required: No + +#### `politeness`: `'polite' | 'assertive'` + +A politeness level for the notice's spoken message. Should be provided as one of the valid options for [an `aria-live` attribute value](https://www.w3.org/TR/wai-aria-1.1/#aria-live). Note that this value should be considered a suggestion; assistive technologies may override it based on internal heuristics. + +A value of `'assertive'` is to be used for important, and usually time-sensitive, information. It will interrupt anything else the screen reader is announcing in that moment. + +A value of `'polite'` is to be used for advisory information. It should not interrupt what the screen reader is announcing in that moment (the "speech queue") or interrupt the current task. + +- Required: No +- Default: `'polite'` + +#### `spokenMessage`: `string` + +Used to provide a custom spoken message. + +- Required: No +- Default: `children` ## Related components diff --git a/packages/components/src/snackbar/index.js b/packages/components/src/snackbar/index.tsx similarity index 68% rename from packages/components/src/snackbar/index.js rename to packages/components/src/snackbar/index.tsx index 6a67f055e9ed1f..833d9dce8d30ff 100644 --- a/packages/components/src/snackbar/index.js +++ b/packages/components/src/snackbar/index.tsx @@ -1,6 +1,7 @@ /** * External dependencies */ +import type { ForwardedRef, KeyboardEvent, MouseEvent } from 'react'; import classnames from 'classnames'; /** @@ -14,21 +15,23 @@ import warning from '@wordpress/warning'; /** * Internal dependencies */ -import { Button } from '../'; +import Button from '../button'; +import type { NoticeAction, SnackbarProps } from './types'; +import type { WordPressComponentProps } from '../ui/context'; -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. + * @param message Message to announce. + * @param politeness Politeness to announce. */ -function useSpokenMessage( message, politeness ) { +function useSpokenMessage( + message: SnackbarProps[ 'spokenMessage' ], + politeness: NonNullable< SnackbarProps[ 'politeness' ] > +) { const spokenMessage = typeof message === 'string' ? message : renderToString( message ); @@ -39,42 +42,43 @@ function useSpokenMessage( message, politeness ) { }, [ spokenMessage, politeness ] ); } -function Snackbar( +function UnforwardedSnackbar( { className, children, spokenMessage = children, politeness = 'polite', actions = [], - onRemove = noop, + onRemove, icon = null, explicitDismiss = false, // onDismiss is a callback executed when the snackbar is dismissed. // It is distinct from onRemove, which _looks_ like a callback but is // actually the function to call to remove the snackbar from the UI. - onDismiss = noop, + onDismiss, listRef, - }, - ref + }: WordPressComponentProps< SnackbarProps, 'div' >, + ref: ForwardedRef< any > ) { - onDismiss = onDismiss || noop; - - function dismissMe( event ) { + function dismissMe( event: KeyboardEvent | MouseEvent ) { if ( event && event.preventDefault ) { event.preventDefault(); } // Prevent focus loss by moving it to the list element. - listRef.current.focus(); + listRef?.current?.focus(); - onDismiss(); - onRemove(); + onDismiss?.(); + onRemove?.(); } - function onActionClick( event, onClick ) { + function onActionClick( + event: MouseEvent, + onClick: NoticeAction[ 'onClick' ] + ) { event.stopPropagation(); - onRemove(); + onRemove?.(); if ( onClick ) { onClick( event ); @@ -87,8 +91,8 @@ function Snackbar( useEffect( () => { const timeoutHandle = setTimeout( () => { if ( ! explicitDismiss ) { - onDismiss(); - onRemove(); + onDismiss?.(); + onRemove?.(); } }, NOTICE_TIMEOUT ); @@ -118,10 +122,10 @@ function Snackbar(